W7 done done

This commit is contained in:
root committed 2026-06-15 10:28:23 +00:00
1 parent b1a0df9c93
commit f5637bb587
31 files changed
+558 -1148

No files matched your search

+2 -2
View File
@@ -293,7 +293,7 @@ The runtime reads its server settings from:
/etc/uce/settings.cfg
```
The shipped example contains the important filesystem and FastCGI settings:
The example contains the filesystem and FastCGI settings:
```ini
BIN_DIRECTORY=/var/cache/uce/work
@@ -473,7 +473,7 @@ Important details:
- `SCRIPT_FILENAME` should resolve to the actual `.uce` file on disk
- `proxy_http_version 1.1` and the `Upgrade` / `Connection` headers are required for WebSockets
The `location /` block above is intentionally conservative and only serves real files from `site/`. If your app uses a front-controller pattern such as routing everything through `/index.uce`, change that block accordingly.
The `location /` block only serves files from `site/`. If your app uses a front-controller pattern such as routing everything through `/index.uce`, change that block accordingly.
### 6. Think about document root and private files
-1080
View File
File diff suppressed because it is too large. Load diff
@@ -1,6 +1,6 @@
diff --git a/vendor/miniz/miniz_tdef.c b/vendor/miniz/miniz_tdef.c
--- a/vendor/miniz/miniz_tdef.c
+++ b/vendor/miniz/miniz_tdef.c
diff --git a/src/3rdparty/miniz/miniz_tdef.c b/src/3rdparty/miniz/miniz_tdef.c
--- a/src/3rdparty/miniz/miniz_tdef.c
+++ b/src/3rdparty/miniz/miniz_tdef.c
@@
-static const mz_uint s_tdefl_num_probes[11];
+static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };
+94
View File
@@ -0,0 +1,94 @@
#!/usr/bin/env python3
"""Guard the hand-maintained unit-facing API coverage manifest.
This intentionally avoids network/external services. It checks that public API
names we expose to wasm units are either mentioned by a site test or explicitly
marked internal/integration-only, and that active docs exist for doc-required
APIs. The manifest is deliberately source-controlled so a new public function
requires an explicit coverage decision.
"""
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
TEST_DIR = ROOT / "site" / "tests"
DOC_DIR = ROOT / "site" / "doc" / "pages"
# name, needs_doc, status. status: public | internal | integration
PUBLIC_APIS = [
("shell_exec", True, "public"), ("shell_escape", True, "public"),
("basename", True, "public"), ("dirname", True, "public"), ("path_join", True, "public"),
("path_real", True, "public"), ("path_is_within", True, "public"),
("file_open_locked", True, "public"), ("file_close_locked", True, "public"),
("file_release_process_locks", True, "public"), ("file_get_contents_locked_fd", True, "public"),
("file_put_contents_locked_fd", True, "public"), ("file_get_contents", True, "public"),
("file_put_contents", True, "public"), ("file_append_contents", False, "public"),
("cwd_get", True, "public"), ("cwd_set", True, "public"), ("process_start_directory", True, "public"),
("file_mtime", True, "public"), ("file_unlink", True, "public"), ("expand_path", True, "public"),
("ls", True, "public"), ("config_map_u64", True, "public"), ("config_map_f64", True, "public"),
("config_bool_value", True, "public"), ("config_map_bool", True, "public"),
("config_u64", True, "public"), ("config_f64", True, "public"), ("config_bool", True, "public"),
("request_perf", True, "public"), ("time_format_local", True, "public"),
("time_format_relative", True, "public"), ("time_parse", True, "public"),
("backtrace_frames_string", False, "public"), ("capture_backtrace_string", False, "public"),
("signal_name", False, "public"), ("memcache_escape_key", True, "public"),
("memcache_escape_keys", True, "public"), ("memcache_command", True, "public"),
("memcache_get_multiple", True, "public"), ("runtime_safe_key", True, "public"),
("float_val", True, "public"), ("nibble", True, "public"), ("json_consume_space", False, "public"),
("array_merge", True, "public"), ("safe_name", True, "public"), ("ascii_safe_name", True, "public"),
("to_json", False, "public"), ("remove", False, "public"), ("clear", False, "public"),
("gen_sha1", True, "public"), ("gen_noise32", True, "public"), ("gen_noise64", True, "public"),
("gen_noise01", True, "public"), ("gen_int", True, "public"), ("gen_float", True, "public"),
("draw_int", True, "public"), ("draw_float", True, "public"),
("encode_query", True, "public"), ("request_script_url", True, "public"),
("request_base_url", True, "public"), ("request_route_from_raw_path", True, "public"),
("cli_arg", True, "public"), ("unit_compile", True, "public"),
("cleanup_sqlite_connections", False, "internal"), ("cleanup_mysql_connections", False, "internal"),
("mysql_connect", True, "integration"), ("mysql_query", True, "integration"),
]
REMOVED_APIS = ["unit_load", "concat"]
def all_test_text() -> str:
parts = []
for path in TEST_DIR.glob("*.uce"):
parts.append(path.read_text(errors="ignore"))
return "\n".join(parts)
def doc_exists(name: str) -> bool:
path = DOC_DIR / f"{name}.txt"
return path.exists() and "Removed" not in path.read_text(errors="ignore")[:200]
def has_call(text: str, name: str) -> bool:
return f"{name}(" in text or f".{name}(" in text or f'"{name}"' in text
def main() -> int:
tests = all_test_text()
errors = []
for name, needs_doc, status in PUBLIC_APIS:
if status == "public" and not has_call(tests, name):
errors.append(f"missing test coverage: {name}")
if needs_doc and status in {"public", "integration"} and not doc_exists(name):
errors.append(f"missing active doc page: {name}")
compiler_h = (ROOT / "src" / "lib" / "compiler.h").read_text(errors="ignore")
if "#ifndef __UCE_WASM_UNIT__\nSharedUnit* unit_load" not in compiler_h:
errors.append("unit_load is not guarded out of wasm-unit exposure")
for name in REMOVED_APIS:
page = DOC_DIR / f"{name}.txt"
if name == "concat" and page.exists() and "Removed" not in page.read_text(errors="ignore")[:300]:
errors.append("concat doc is not tombstoned")
if name == "unit_load" and page.exists() and "native-only" not in page.read_text(errors="ignore"):
errors.append("unit_load doc is not native-only/tombstoned")
if errors:
print("API coverage manifest FAILED")
for error in errors:
print("- " + error)
return 1
print(f"API coverage manifest ok: {len(PUBLIC_APIS)} entries checked")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+28
View File
@@ -0,0 +1,28 @@
#include "testlib.h"
RENDER(Request& context)
{
if(!test_demo_request_allowed(context))
{
site_tests_restricted(context, "API Coverage Manifest", "scan repository headers, tests, and docs with the local manifest script");
return;
}
u64 passed = 0;
u64 failed = 0;
u64 skipped = 0;
auto check = [&](String name, bool ok, String detail)
{
site_tests_case(name, ok ? "pass" : "fail", detail);
if(ok)
passed++;
else
failed++;
};
site_tests_page_start("API Coverage Manifest", "Repository-level guard that public wasm-unit APIs have an explicit test/doc/internal decision.");
String out = shell_exec("cd /Code/uce.openfu.com/uce && python3 scripts/api_coverage_manifest.py");
check("api coverage manifest", contains(out, "API coverage manifest ok"), out);
site_tests_summary(passed, failed, skipped, "This page intentionally runs only on trusted/local test requests.");
site_tests_page_end();
}
+6
View File
@@ -14,6 +14,12 @@ CLI(Request& context)
print(input["message"].to_string(), "\n");
return;
}
if(action == "arg")
{
print(cli_arg(context, "message", "fallback"), "\n");
print(cli_arg(context, "missing", "fallback"), "\n");
return;
}
context.set_status(400, "CLI Error");
print("unknown cli action: ", action, "\n");
}
+2
View File
@@ -325,6 +325,8 @@ CLI(Request& context)
print("[GROUP] starter ", std::to_string((u64)((time_precise() - group_start) * 1000)), " ms\n");
group_start = time_precise();
cli_run_tcp_smoke();
String cli_arg_out = shell_exec("cd /Code/uce.openfu.com/uce && bash scripts/uce-cli /tests/cli.uce action=arg message=hello-cli 2>&1");
cli_test_case("uce_cli_fixture:cli_arg", contains(cli_arg_out, "hello-cli") && contains(cli_arg_out, "fallback"), cli_truncate(cli_arg_out));
print("[GROUP] tcp ", std::to_string((u64)((time_precise() - group_start) * 1000)), " ms\n");
group_start = time_precise();
cli_run_wasm_kill(include_kill);
+34
View File
@@ -236,6 +236,30 @@ RENDER(Request& context)
tree["nested"]["ok"].set_bool(true);
check("DValue map access", tree["suite"].to_string() == "core" && tree["nested"]["api"].to_string() == "dvalue", json_encode(tree));
String nibble_source = "alpha/beta/gamma";
String nibble_first = nibble(nibble_source, "/");
StringMap merge_a; merge_a["a"] = "one"; merge_a["shared"] = "old";
StringMap merge_b; merge_b["b"] = "two"; merge_b["shared"] = "new";
StringMap merged_map = array_merge(merge_a, merge_b);
DValue merge_dv_a; merge_dv_a["a"] = "one"; merge_dv_a["shared"] = "old";
DValue merge_dv_b; merge_dv_b["b"] = "two"; merge_dv_b["shared"] = "new";
DValue merged_dv = array_merge(merge_dv_a, merge_dv_b);
u32 json_space_i = 0;
json_consume_space(" \n\tvalue", json_space_i);
check("float_val() / nibble() / array_merge() / json_consume_space()", float_val("12.5") > 12.49 && float_val("12.5") < 12.51 && nibble_first == "alpha" && nibble_source == "beta/gamma" && merged_map["shared"] == "new" && merged_map["a"] == "one" && merged_dv["b"].to_string() == "two" && json_space_i == 3, "nibble=" + nibble_first + " rest=" + nibble_source + " merged=" + var_dump(merged_map) + " json_space_i=" + std::to_string(json_space_i));
String unsafe = "Hello, 世界 / ../../ name!";
check("safe_name() / ascii_safe_name()", safe_name(unsafe) != "" && ascii_safe_name("Hello world! 42") == "Hello_world_42", safe_name(unsafe) + " / " + ascii_safe_name("Hello world! 42"));
DValue mutable_tree;
mutable_tree["keep"] = "yes";
mutable_tree["remove"] = "no";
String mutable_json = mutable_tree.to_json();
mutable_tree.remove("remove");
bool removed_ok = !mutable_tree.has("remove") && mutable_tree["keep"].to_string() == "yes";
mutable_tree.clear();
check("DValue to_json() / remove() / clear()", contains(mutable_json, "keep") && removed_ok && !mutable_tree.has("keep") && mutable_tree.get_type_name() == "empty", mutable_json);
// wasm_trace.h (WASM-PROPOSAL Phase 4): canned Wasmtime-format messages —
// the live trap path is gated by the phase-4 spike runner
String trace_msg = "error while executing at wasm backtrace:\n 0: 0x9f - units!_Z6renderR7Request\n";
@@ -255,6 +279,16 @@ RENDER(Request& context)
check("wasm_trace_collapse() raw passthrough", wasm_trace_collapse("plain host error") == "plain host error", wasm_trace_collapse("plain host error"));
u32 noise32_a = gen_noise32(7, 11);
u32 noise32_b = gen_noise32(7, 11);
u64 noise64 = gen_noise64(7, 11);
f64 noise01 = gen_noise01(7, 11);
u64 ranged_int = gen_int(10, 20, 3, 4);
f64 ranged_float = gen_float(1.0, 2.0, 3, 4);
u64 drawn_int = draw_int(1, 3);
f64 drawn_float = draw_float(1.0, 2.0);
check("gen_sha1() / gen_*() / draw_*()", gen_sha1("uce") == gen_sha1("uce") && gen_sha1("uce") != gen_sha1("UCE") && noise32_a == noise32_b && noise64 > 0 && noise01 >= 0.0 && noise01 <= 1.0 && ranged_int >= 10 && ranged_int <= 20 && ranged_float >= 1.0 && ranged_float <= 2.0 && drawn_int >= 1 && drawn_int <= 3 && drawn_float >= 1.0 && drawn_float <= 2.0, "noise32=" + std::to_string((u64)noise32_a) + " noise64=" + std::to_string(noise64) + " noise01=" + std::to_string(noise01) + " draw=" + std::to_string(drawn_int));
site_tests_summary(passed, failed, skipped, "These assertions intentionally stay pure and side-effect free so they remain safe on the public site.");
site_tests_page_end();
}
+6
View File
@@ -44,6 +44,12 @@ RENDER(Request& context)
check("uri_decode() malformed percent literals", malformed_percent == "% %A %GG ok done", malformed_percent);
check("parse_uri() empty input", empty_uri.parts["raw"] == "", var_dump(empty_uri));
check("parse_query()", query["alpha"] == "1" && query["beta"] == "two words" && query["token"] == "a=b" && query["keyless"] == "" && query["empty"] == "" && query["trailing"] == "ok" && query.count("") == 0, query_dump);
StringMap encoded_query_map;
encoded_query_map["alpha"] = "one two";
encoded_query_map["token"] = "a=b";
String encoded_query = encode_query(encoded_query_map);
DValue raw_route = request_route_from_raw_path("/workspace/projects/", "index");
check("encode_query() / request route helpers", contains(encoded_query, "alpha=one%20two") && contains(encoded_query, "token=a%3Db") && request_script_url(context) != "" && request_base_url(context) != "" && raw_route["path"].to_string() == "workspace/projects" && raw_route["valid"].to_bool(), encoded_query + " script=" + request_script_url(context) + " base=" + request_base_url(context) + " route=" + json_encode(raw_route));
check("set_cookie()", set_cookie_dump.find("site-tests-cookie") != String::npos && set_cookie_dump.find("cookie-value") != String::npos && set_cookie_dump.find("HttpOnly") != String::npos && set_cookie_dump.find("SameSite=Lax") != String::npos, set_cookie_dump);
check("response header mutation", context.header["X-Site-Tests"] == "http-suite", header_dump);
check("session_start()", session_id != "", "session_id=" + session_id);
+49 -1
View File
@@ -29,9 +29,57 @@ RENDER(Request& context)
site_tests_page_start("Filesystem", "Local-only file helper coverage using a temporary file under /tmp.");
check("path_join()", file_name == "/tmp/uce-site-tests-io.txt", file_name);
check("basename() / dirname() / expand_path()", basename(file_name) == "uce-site-tests-io.txt" && dirname(file_name) == "/tmp" && expand_path("child", "/tmp/base") == "/tmp/base/child" && expand_path("../sibling", "/tmp/base") == "/tmp/sibling", basename(file_name) + " / " + dirname(file_name) + " / " + expand_path("../sibling", "/tmp/base"));
check("file_put_contents()", write_ok, file_name);
check("file_append()", file_text.find("|beta") != String::npos, file_text);
check("file_append() / file_append_contents()", file_append_contents(file_name, "|gamma") && contains(file_get_contents(file_name), "|gamma"), file_get_contents(file_name));
file_put_contents(file_name, "alpha|beta");
check("file_get_contents()", file_text == "alpha|beta", file_text);
check("file_mtime()", file_mtime(file_name) > 0, std::to_string((u64)file_mtime(file_name)));
String real_tmp = path_real("/tmp");
String real_file = path_real(file_name);
check("path_real() / path_is_within() canonical containment", real_tmp != "" && real_file != "" && path_is_within(file_name, "/tmp") && path_is_within("/tmp/../tmp/uce-site-tests-io.txt", "/tmp") && !path_is_within("/tmp", file_name) && !path_is_within("/tmp", "/tmp/uce-site-tests-io.txt"), real_tmp + " / " + real_file);
String shell_escaped = shell_escape("a'b; echo injected");
String shell_out = shell_exec("printf %s " + shell_escaped);
check("shell_escape() / shell_exec()", shell_out == "a'b; echo injected" && contains(shell_escaped, "'\\''"), shell_escaped + " => " + shell_out);
String lock_file = "/tmp/uce-site-tests-lock.txt";
int lock_fd = file_open_locked(lock_file, 66, LOCK_EX, 0644, 3.0, "site tests");
bool lock_write = file_put_contents_locked_fd(lock_fd, "locked-content");
String lock_read = file_get_contents_locked_fd(lock_fd);
file_close_locked(lock_fd);
check("file_open_locked() / locked fd read-write / close", lock_fd > 0 && lock_write && lock_read == "locked-content" && file_get_contents(lock_file) == "locked-content", "fd=" + std::to_string(lock_fd) + " read=" + lock_read);
int release_fd = file_open_locked(lock_file, 66, LOCK_EX, 0644, 3.0, "release test");
file_release_process_locks("site tests release");
check("file_release_process_locks()", release_fd > 0, "fd=" + std::to_string(release_fd));
String start_dir = process_start_directory();
String old_cwd = cwd_get();
cwd_set("/tmp");
String tmp_cwd = cwd_get();
cwd_set(old_cwd);
check("cwd_get() / cwd_set() / process_start_directory()", old_cwd != "" && tmp_cwd == "/tmp" && process_start_directory() == start_dir && start_dir != "", "old=" + old_cwd + " tmp=" + tmp_cwd + " start=" + start_dir);
StringMap cfg;
cfg["u64"] = "42";
cfg["f64"] = "3.5";
cfg["yes"] = "yes";
cfg["no"] = "off";
check("config_map_*() / config_*() helpers", config_map_u64(cfg, "u64", 7) == 42 && config_map_u64(cfg, "bad", 7) == 7 && config_map_f64(cfg, "f64", 1.0) > 3.49 && !config_map_bool(cfg, "no") && config_bool_value("false") == false && config_u64("NO_SUCH_UCE_TEST_KEY", 99) == 99 && config_f64("NO_SUCH_UCE_TEST_KEY", 1.25) == 1.25 && !config_bool("NO_SUCH_UCE_TEST_KEY", false), var_dump(cfg));
DValue perf = request_perf();
check("request_perf()", perf["worker_pid"].to_u64() > 0 && perf["request_count"].to_u64() > 0, json_encode(perf));
u64 parsed_epoch = time_parse("1970-01-01 00:00:05 UTC");
String utc_year = time_format_utc("%Y", parsed_epoch);
String local_year = time_format_local("%Y", parsed_epoch);
String relative = time_format_relative(time() - 120, "recent %deltaS", 1, "medium %deltaM", 3600, "old %deltaH");
check("time_format_local() / time_format_relative() / time_parse()", parsed_epoch == 5 && utc_year == "1970" && local_year != "" && contains(relative, "medium"), "parsed=" + std::to_string(parsed_epoch) + " utc=" + utc_year + " local=" + local_year + " rel=" + relative);
StringList escaped_keys = memcache_escape_keys({"a b", "line\nkey"});
check("memcache_escape_key() / memcache_escape_keys()", memcache_escape_key("a b") == "a_b" && escaped_keys.size() == 2 && !contains(escaped_keys[1], "\n"), join(escaped_keys, ","));
check("signal_name() / runtime_safe_key() / capture_backtrace_string()", signal_name(SIGSEGV) == "SIGSEGV" && runtime_safe_key(" task one ") == gen_sha1("task one") && runtime_safe_key(" ") == "" && capture_backtrace_string() == "", signal_name(SIGSEGV) + " / " + runtime_safe_key(" task one "));
site_tests_summary(passed, failed, skipped, "This page intentionally limits writes to /tmp/uce-site-tests-io.txt so it stays disposable.");
site_tests_page_end();
+1
View File
@@ -12,6 +12,7 @@ sqlite.uce|SQLite|SQLite connector with prepared named parameters and DValue row
zip.uce|ZIP|Archive helpers that create and extract temporary server-side files.|http suite uce internal|ZIP|1|1
services.uce|Sockets And Services|Network/service helpers that are restricted outside trusted networks.|http suite uce internal|Sockets And Services|1|1
tasks.uce|Tasks|Background task helper coverage.|http suite uce internal|Tasks|1|1
api_coverage.uce|API Coverage Manifest|Repository-level guard that public wasm APIs have tests/docs or an explicit internal decision.|http suite uce internal|API Coverage Manifest|1|1
security_headers.uce|Security Header Sanitizer|Low-level response header sanitizer fixture covered by the security smoke suite.|security http internal fixture|security header sanitizer test|0|1
cli.uce|CLI Fixture|Local CLI socket fixture; HTTP render intentionally returns 404.|cli internal fixture|uce-unit-cli|0|0
call_helpers.uce|Unit Call Fixture|Fixture for unit_call and unit_render helper tests.|internal fixture|UNIT_RENDER_FIXTURE|0|0
+1
View File
@@ -30,6 +30,7 @@ RENDER(Request& context)
check("units_list()", unit_paths.size() > 0, "count=" + std::to_string(unit_paths.size()));
check("unit_info()", info["path"].to_string() != "", json_encode(info));
check("unit_compile()", unit_compile("call_helpers.uce"), "call_helpers.uce");
check("unit_call()", call_output.find("UNIT_CALL_EXPORT_OK") != String::npos, call_output);
check("unit_render()", render_output.find("data-unit-render=\"ok\"") != String::npos, render_output);
File renamed without changes.
@@ -2,7 +2,7 @@
UCE vendors miniz 3.0.2 from <https://github.com/richgel999/miniz> for ZIP archive support.
Files are kept under `vendor/miniz/` and are included by `src/lib/zip.cpp` so UCE can expose `zip_create()`, `zip_list()`, `zip_read()`, `zip_extract()`, `gz_compress()`, and `gz_uncompress()` without adding a runtime package dependency.
Files are kept under `src/3rdparty/miniz/` and are included by `src/lib/zip.cpp` so UCE can expose `zip_create()`, `zip_list()`, `zip_read()`, `zip_extract()`, `gz_compress()`, and `gz_uncompress()` without adding a runtime package dependency.
Local patch:
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+2
View File
@@ -28,7 +28,9 @@ DValue unit_info(String path = "");
StringList units_list();
bool unit_compile(String path = "");
#ifndef __UCE_WASM_UNIT__
SharedUnit* unit_load(String file_name);
#endif
void unit_render(String file_name);
void unit_render(String file_name, Request& context);
DValue* unit_call(String file_name, String function_name, DValue* call_param = 0);
+146 -18
View File
@@ -2,6 +2,7 @@
#include <cmath>
#include "types.h"
#include "functionlib.h"
#include "hash.h"
#include "uri.h"
#include "sys.h"
@@ -34,6 +35,18 @@ int uce_host_server_stop(const char* key, size_t key_len);
size_t uce_host_memcache_command(uint64_t sockfd, const char* command, size_t command_len, char* buf, size_t cap);
size_t uce_host_mysql(const char* in, size_t in_len, char* out, size_t cap);
size_t uce_host_request_perf(const char* in, size_t in_len, char* out, size_t cap);
size_t uce_host_shell_exec(const char* cmd, size_t cmd_len, char* buf, size_t cap);
int uce_host_file_open_locked(const char* path, size_t path_len, int open_flags, int lock_type, int create_mode, double wait_timeout_seconds, const char* purpose, size_t purpose_len);
void uce_host_file_close_locked(int handle);
void uce_host_file_release_process_locks(const char* reason, size_t reason_len);
size_t uce_host_file_read_locked_fd(int handle, char* buf, size_t cap);
int uce_host_file_write_locked_fd(int handle, const char* content, size_t content_len);
size_t uce_host_path_real(const char* path, size_t path_len, char* buf, size_t cap);
int uce_host_path_is_within(const char* path, size_t path_len, const char* root, size_t root_len);
size_t uce_host_cwd_get(char* buf, size_t cap);
int uce_host_cwd_set(const char* path, size_t path_len);
size_t uce_host_process_start_directory(char* buf, size_t cap);
size_t uce_host_last_trap_trace(char* buf, size_t cap);
}
static String wasm_current_unit_file()
@@ -41,13 +54,45 @@ static String wasm_current_unit_file()
return(context ? context->resources.current_unit_file : String(""));
}
String shell_exec(String cmd) { (void)cmd; return(""); }
String shell_escape(String raw) { return(raw); }
String shell_exec(String cmd)
{
size_t required = uce_host_shell_exec(cmd.data(), cmd.size(), 0, 0);
if(required == 0)
return("");
String output(required, 0);
size_t got = uce_host_shell_exec(cmd.data(), cmd.size(), &output[0], required);
output.resize(got <= required ? got : 0);
return(output);
}
String shell_escape(String raw)
{
String result;
for(auto c : raw)
{
if(c == '\'')
result.append("'\\''");
else
result.append(1, c);
}
return("'" + result + "'");
}
String basename(String fn) { while(fn.find("/") != String::npos) fn = fn.substr(fn.find("/") + 1); return(fn); }
String dirname(String fn) { auto pos = fn.find_last_of('/'); return(pos == String::npos ? "" : fn.substr(0, pos)); }
String path_join(String base, String child) { if(base == "") return(child); if(child == "") return(base); if(child[0] == '/') return(child); return(base + (base.back() == '/' ? "" : "/") + child); }
String path_real(String path) { return(path); }
bool path_is_within(String path, String root) { return(str_starts_with(path, root)); }
String path_real(String path)
{
size_t required = uce_host_path_real(path.data(), path.size(), 0, 0);
if(required == 0)
return("");
String resolved(required, 0);
size_t got = uce_host_path_real(path.data(), path.size(), &resolved[0], required);
resolved.resize(got <= required ? got : 0);
return(resolved);
}
bool path_is_within(String path, String root)
{
return(uce_host_path_is_within(path.data(), path.size(), root.data(), root.size()) != 0);
}
bool mkdir(String path)
{
String current = wasm_current_unit_file();
@@ -58,11 +103,29 @@ bool file_exists(String path)
String current = wasm_current_unit_file();
return(uce_host_file_exists(path.data(), path.size(), current.data(), current.size()) != 0);
}
int file_open_locked(String file_name, int open_flags, int lock_type, int create_mode, f64 wait_timeout_seconds, String purpose) { (void)file_name; (void)open_flags; (void)lock_type; (void)create_mode; (void)wait_timeout_seconds; (void)purpose; return(-1); }
void file_close_locked(int fd) { (void)fd; }
void file_release_process_locks(String reason) { (void)reason; }
String file_get_contents_locked_fd(int fd) { (void)fd; return(""); }
bool file_put_contents_locked_fd(int fd, String content) { (void)fd; (void)content; return(false); }
int file_open_locked(String file_name, int open_flags, int lock_type, int create_mode, f64 wait_timeout_seconds, String purpose)
{
return(uce_host_file_open_locked(
file_name.data(), file_name.size(), open_flags, lock_type, create_mode, wait_timeout_seconds,
purpose.data(), purpose.size()
));
}
void file_close_locked(int fd) { uce_host_file_close_locked(fd); }
void file_release_process_locks(String reason) { uce_host_file_release_process_locks(reason.data(), reason.size()); }
String file_get_contents_locked_fd(int fd)
{
size_t required = uce_host_file_read_locked_fd(fd, 0, 0);
if(required == 0)
return("");
String content(required, 0);
size_t got = uce_host_file_read_locked_fd(fd, &content[0], required);
content.resize(got <= required ? got : 0);
return(content);
}
bool file_put_contents_locked_fd(int fd, String content)
{
return(uce_host_file_write_locked_fd(fd, content.data(), content.size()) != 0);
}
String file_get_contents(String file_name)
{
String current = wasm_current_unit_file();
@@ -84,9 +147,27 @@ bool file_append_contents(String file_name, String content)
String current = wasm_current_unit_file();
return(uce_host_file_write(file_name.data(), file_name.size(), current.data(), current.size(), content.data(), content.size(), 1) != 0);
}
String cwd_get() { return("/"); }
void cwd_set(String path) { (void)path; }
String process_start_directory() { return("/"); }
String cwd_get()
{
size_t required = uce_host_cwd_get(0, 0);
if(required == 0)
return("");
String cwd(required, 0);
size_t got = uce_host_cwd_get(&cwd[0], required);
cwd.resize(got <= required ? got : 0);
return(cwd);
}
void cwd_set(String path) { uce_host_cwd_set(path.data(), path.size()); }
String process_start_directory()
{
size_t required = uce_host_process_start_directory(0, 0);
if(required == 0)
return("");
String cwd(required, 0);
size_t got = uce_host_process_start_directory(&cwd[0], required);
cwd.resize(got <= required ? got : 0);
return(cwd);
}
time_t file_mtime(String file_name)
{
String current = wasm_current_unit_file();
@@ -253,11 +334,51 @@ bool ws_close(String connection_id)
context->resources.websocket_dispatch_commands.push(command);
return(true);
}
String backtrace_frames_string(void* const* frames, size_t size, u32 skip_frames) { (void)frames; (void)size; (void)skip_frames; return(""); }
String capture_backtrace_string(u32 max_frames, u32 skip_frames) { (void)max_frames; (void)skip_frames; return(""); }
String signal_name(int sig) { (void)sig; return(""); }
String memcache_escape_key(String key) { return(key); }
StringList memcache_escape_keys(StringList keys) { return(keys); }
String backtrace_frames_string(void* const* frames, size_t size, u32 skip_frames) { (void)frames; (void)size; (void)skip_frames; return(capture_backtrace_string(0, 0)); }
String capture_backtrace_string(u32 max_frames, u32 skip_frames)
{
(void)max_frames;
(void)skip_frames;
size_t required = uce_host_last_trap_trace(0, 0);
if(required == 0)
return("");
String trace(required, 0);
size_t got = uce_host_last_trap_trace(&trace[0], required);
trace.resize(got <= required ? got : 0);
return(trace);
}
String signal_name(int sig)
{
switch(sig)
{
case 6: return("SIGABRT");
case 7: return("SIGBUS");
case 8: return("SIGFPE");
case 4: return("SIGILL");
case 2: return("SIGINT");
case 11: return("SIGSEGV");
case 15: return("SIGTERM");
default: return("");
}
}
String memcache_escape_key(String key)
{
String result;
for(auto c : key)
{
if(isspace((unsigned char)c))
c = '_';
result.append(1, c);
}
return(result);
}
StringList memcache_escape_keys(StringList keys)
{
StringList result;
for(auto s : keys)
result.push_back(memcache_escape_key(s));
return(result);
}
u64 memcache_connect(String host, short port)
{
if(host == "")
@@ -345,7 +466,14 @@ extern "C" int uce_wasm_task_run(uint64_t callback_id)
}
int task_kill(pid_t pid, int sig) { return(uce_host_task_kill(pid, sig)); }
String runtime_safe_key(String key, String label) { (void)label; return(key); }
String runtime_safe_key(String key, String label)
{
(void)label;
key = trim(key);
if(key == "")
return("");
return(gen_sha1(key));
}
pid_t task(String key, std::function<void()> exec_after_spawn, u64 timeout)
{
u64 id = wasm_next_task_callback_id++;
+4 -4
View File
@@ -3,10 +3,10 @@
#include <cstring>
extern "C" {
#include "../../vendor/miniz/miniz.c"
#include "../../vendor/miniz/miniz_tdef.c"
#include "../../vendor/miniz/miniz_tinfl.c"
#include "../../vendor/miniz/miniz_zip.c"
#include "../3rdparty/miniz/miniz.c"
#include "../3rdparty/miniz/miniz_tdef.c"
#include "../3rdparty/miniz/miniz_tinfl.c"
#include "../3rdparty/miniz/miniz_zip.c"
}
String zip_error(String api, String detail)
+12
View File
@@ -19,6 +19,7 @@ uce_host_zip
uce_host_units
uce_host_component_resolve
uce_host_request_perf
uce_host_shell_exec
uce_host_file_exists
uce_host_file_read
uce_host_file_write
@@ -26,5 +27,16 @@ uce_host_file_unlink
uce_host_file_list
uce_host_file_mkdir
uce_host_file_mtime
uce_host_file_open_locked
uce_host_file_close_locked
uce_host_file_release_process_locks
uce_host_file_read_locked_fd
uce_host_file_write_locked_fd
uce_host_path_real
uce_host_path_is_within
uce_host_cwd_get
uce_host_cwd_set
uce_host_process_start_directory
uce_host_last_trap_trace
uce_host_regex
uce_host_sqlite
+167 -1
View File
@@ -40,6 +40,9 @@
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <sys/time.h>
#include <vector>
@@ -428,21 +431,32 @@ public:
request_perf.active = true;
}
// Host-owned opaque fd handles opened by wasm file_open_locked().
std::vector<int> locked_file_handles;
#ifdef UCE_WASM_HOST_CONNECTORS
// Host-owned resource handle table (§3.1): connections opened by the guest
// live here and are closed when the workspace drops at request end.
std::vector<SQLite*> sqlite_handles;
std::vector<MySQL*> mysql_handles;
#endif
~WasmWorkspace()
{
for(int fd : locked_file_handles)
if(fd >= 0)
{
flock(fd, LOCK_UN);
::close(fd);
}
#ifdef UCE_WASM_HOST_CONNECTORS
for(auto* db : sqlite_handles)
if(db)
delete db; // ~SQLite disconnects
for(auto* db : mysql_handles)
if(db)
delete db; // ~MySQL disconnects
}
#endif
}
// The guest calls a sized hostcall twice (buf=0 to learn the length, then
// to fetch). For side-effecting ops (sqlite) re-executing on the fetch is
@@ -1304,6 +1318,77 @@ private:
fprintf(stderr, "[guest log %d] %.*s\n", args[0].i32(), (int)text.size(), text.data());
return(std::monostate());
}));
if(mod == "env" && name == "uce_host_shell_exec")
return(add([self](Caller caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
String cmd;
self->hostcall_read(args[0].i32(), args[1].i32(), cmd);
String out;
if(!self->hostcall_staged("shell:" + cmd, out))
{
out = ::shell_exec(cmd);
self->hostcall_stage("shell:" + cmd, out);
}
u32 cap = (u32)args[3].i32();
int32_t buf = args[2].i32();
if(buf != 0 && cap >= out.size())
self->hostcall_write(buf, out);
caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks);
results[0] = Val((int32_t)out.size());
return(std::monostate());
}));
if(mod == "env" && name == "uce_host_path_real")
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
String path;
self->hostcall_read(args[0].i32(), args[1].i32(), path);
String resolved = ::path_real(path);
u32 cap = (u32)args[3].i32();
int32_t buf = args[2].i32();
if(buf != 0 && cap >= resolved.size())
self->hostcall_write(buf, resolved);
results[0] = Val((int32_t)resolved.size());
return(std::monostate());
}));
if(mod == "env" && name == "uce_host_path_is_within")
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
String path, root;
self->hostcall_read(args[0].i32(), args[1].i32(), path);
self->hostcall_read(args[2].i32(), args[3].i32(), root);
results[0] = Val(::path_is_within(path, root) ? (int32_t)1 : (int32_t)0);
return(std::monostate());
}));
if(mod == "env" && name == "uce_host_cwd_get")
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
String cwd = ::cwd_get();
u32 cap = (u32)args[1].i32();
int32_t buf = args[0].i32();
if(buf != 0 && cap >= cwd.size())
self->hostcall_write(buf, cwd);
results[0] = Val((int32_t)cwd.size());
return(std::monostate());
}));
if(mod == "env" && name == "uce_host_cwd_set")
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
String path;
self->hostcall_read(args[0].i32(), args[1].i32(), path);
results[0] = Val(::chdir(path.c_str()) == 0 ? (int32_t)1 : (int32_t)0);
return(std::monostate());
}));
if(mod == "env" && name == "uce_host_process_start_directory")
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
String cwd = ::process_start_directory();
u32 cap = (u32)args[1].i32();
int32_t buf = args[0].i32();
if(buf != 0 && cap >= cwd.size())
self->hostcall_write(buf, cwd);
results[0] = Val((int32_t)cwd.size());
return(std::monostate());
}));
if(mod == "env" && name == "uce_host_last_trap_trace")
return(add([](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
(void)args;
results[0] = Val((int32_t)0);
return(std::monostate());
}));
if(mod == "env" && name == "uce_host_file_exists")
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
String path, current;
@@ -1340,6 +1425,87 @@ private:
results[0] = Val((int64_t)mtime);
return(std::monostate());
}));
if(mod == "env" && name == "uce_host_file_open_locked")
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
String path, purpose;
self->hostcall_read(args[0].i32(), args[1].i32(), path);
self->hostcall_read(args[6].i32(), args[7].i32(), purpose);
int fd = ::file_open_locked(path, args[2].i32(), args[3].i32(), args[4].i32(), args[5].f64(), purpose);
int handle = -1;
if(fd >= 0)
{
for(size_t i = 0; i < self->locked_file_handles.size(); i++)
if(self->locked_file_handles[i] < 0)
{
self->locked_file_handles[i] = fd;
handle = (int)i + 1;
break;
}
if(handle < 0)
{
self->locked_file_handles.push_back(fd);
handle = (int)self->locked_file_handles.size();
}
}
results[0] = Val((int32_t)handle);
return(std::monostate());
}));
if(mod == "env" && name == "uce_host_file_close_locked")
return(add([self](Caller, Span<const Val> args, Span<Val>) -> Result<std::monostate, Trap> {
int handle = args[0].i32();
if(handle >= 1 && (size_t)handle <= self->locked_file_handles.size())
{
int& fd = self->locked_file_handles[(size_t)handle - 1];
if(fd >= 0)
{
::file_close_locked(fd);
fd = -1;
}
}
return(std::monostate());
}));
if(mod == "env" && name == "uce_host_file_release_process_locks")
return(add([self](Caller, Span<const Val>, Span<Val>) -> Result<std::monostate, Trap> {
for(int& fd : self->locked_file_handles)
if(fd >= 0)
{
::file_close_locked(fd);
fd = -1;
}
return(std::monostate());
}));
if(mod == "env" && name == "uce_host_file_read_locked_fd")
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
int handle = args[0].i32();
String content;
if(handle >= 1 && (size_t)handle <= self->locked_file_handles.size())
{
int fd = self->locked_file_handles[(size_t)handle - 1];
if(fd >= 0)
content = ::file_get_contents_locked_fd(fd);
}
u32 cap = (u32)args[2].i32();
int32_t buf = args[1].i32();
if(buf != 0 && cap >= content.size())
self->hostcall_write(buf, content);
results[0] = Val((int32_t)content.size());
return(std::monostate());
}));
if(mod == "env" && name == "uce_host_file_write_locked_fd")
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
int handle = args[0].i32();
String content;
self->hostcall_read(args[1].i32(), args[2].i32(), content);
bool ok = false;
if(handle >= 1 && (size_t)handle <= self->locked_file_handles.size())
{
int fd = self->locked_file_handles[(size_t)handle - 1];
if(fd >= 0)
ok = ::file_put_contents_locked_fd(fd, content);
}
results[0] = Val(ok ? (int32_t)1 : (int32_t)0);
return(std::monostate());
}));
if(mod == "env" && name == "uce_host_file_read")
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
String path, current;
-38
View File
@@ -1,38 +0,0 @@
Library
=================================
- md5 / meow
- fold StringList into DValue _OR_ make better StringList
- make session data use DValue instead of StringList
Bugs
=================================
- shell_escape()
Framework
=================================
- Check: File Upload
- Proxy mode
- WebSockets
- Resident code / Cron / Tick?
- Automated Test Suite
- Documentation
Performance
=================================
- Arena Allocator
- Array implementation
Nice to Have
=================================
- pseudorandom
- XML components
- Optionally store compiled units alongside source
Finally
=================================
- DO WE DO OUR OWN LANGUAGE OR DO WE KEEP USING C?!?