wasm runtime: central WS broker, unified handlers, W7d holdouts, membrane completeness
- WS: a dedicated broker process owns HTTP_PORT + every connection; it forwards renders to the worker pool over uce.sock (non-blocking) and applies ws_* command batches flushed back at workspace teardown. Removes the now-dead per-worker websocket executor (-509 lines). - Dispatch: unify CLI / WebSocket / serve_http / page render through one serve_via_wasm(entry_unit, handler) path; handler string -> __uce_<handler> export symbol. - W7d: rewrite zip.uce to the membrane return-value error contract (no C++ try/catch), error-reporting.uce to genuine wasm traps instead of throw, and sharedunit.uce to unit_info(); empty the native-only token gate. - Membrane: wire ls / mkdir / file_mtime through new uce_host_file_list / uce_host_file_mkdir / uce_host_file_mtime hostcalls (resolve_guest_file gains directory support). Fixes /doc/index.uce listing nothing; adds a regression assertion that the index enumerates items. - Docs: add docs/wasm-runtime-architecture.md; record the W7e staged native- deletion plan in WASM-PROPOSAL.md. Verified: scripts/run_cli_tests.sh --include-wasm-kill -> 87 passed, 0 failed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -155,6 +155,10 @@ void cli_run_demo_smoke()
|
||||
void cli_run_http_smoke()
|
||||
{
|
||||
cli_expect_http("uce_http_smoke:doc index", "/doc/index.uce", 200, "<html>");
|
||||
// Regression guard: the index enumerates pages via ls("pages/"). When that
|
||||
// returned empty (wasm ls() was a stub), the page still rendered <html> but
|
||||
// listed nothing — so assert the function grid is actually populated.
|
||||
cli_expect_http("uce_http_smoke:doc index lists functions", "/doc/index.uce", 200, "func-item");
|
||||
cli_expect_http("uce_http_smoke:doc singlepage", "/doc/singlepage.uce", 200, "<html>");
|
||||
cli_expect_http("uce_http_smoke:doc component page", "/doc/index.uce?p=component", 200, "component()");
|
||||
cli_expect_http("uce_http_smoke:doc regex page", "/doc/index.uce?p=regex_search", 200, "regex_search");
|
||||
|
||||
+14
-34
@@ -49,33 +49,19 @@ RENDER(Request& context)
|
||||
check("zip_read()", hello == "Hello ZIP", hello);
|
||||
check("zip_extract()", extracted && nested == "Nested file" && value == "42", nested + " / " + value);
|
||||
|
||||
bool unsafe_rejected = false;
|
||||
try
|
||||
{
|
||||
DValue unsafe_entries;
|
||||
unsafe_entries["/absolute.txt"] = "bad";
|
||||
zip_create(path_join(base, "unsafe.zip"), unsafe_entries);
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
unsafe_rejected = contains(e.what(), "unsafe");
|
||||
}
|
||||
// Unsafe member names are rejected host-side; the membrane reports the
|
||||
// failure as a false return (no thrown exception crosses into the unit).
|
||||
DValue unsafe_entries;
|
||||
unsafe_entries["/absolute.txt"] = "bad";
|
||||
bool unsafe_rejected = !zip_create(path_join(base, "unsafe.zip"), unsafe_entries);
|
||||
check("zip_create() rejects unsafe names", unsafe_rejected, "absolute member name rejected");
|
||||
|
||||
bool nul_name_rejected = false;
|
||||
try
|
||||
{
|
||||
DValue unsafe_entries;
|
||||
String nul_name = "prefix";
|
||||
nul_name.push_back((char)0x00);
|
||||
nul_name += "suffix.txt";
|
||||
unsafe_entries[nul_name] = "bad";
|
||||
zip_create(path_join(base, "nul-name.zip"), unsafe_entries);
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
nul_name_rejected = contains(e.what(), "unsafe");
|
||||
}
|
||||
DValue nul_entries;
|
||||
String nul_name = "prefix";
|
||||
nul_name.push_back((char)0x00);
|
||||
nul_name += "suffix.txt";
|
||||
nul_entries[nul_name] = "bad";
|
||||
bool nul_name_rejected = !zip_create(path_join(base, "nul-name.zip"), nul_entries);
|
||||
check("zip_create() rejects NUL entry names", nul_name_rejected, "embedded NUL member name rejected");
|
||||
|
||||
String binary_source("UCE", 3);
|
||||
@@ -98,15 +84,9 @@ RENDER(Request& context)
|
||||
check("gz_compress()", gz_body.size() > gz_source.size() && (u8)gz_body[0] == 0x1f && (u8)gz_body[1] == 0x8b, "bytes=" + std::to_string((u64)gz_body.size()));
|
||||
check("gz_uncompress()", gz_roundtrip == gz_source && gz_binary_roundtrip == binary_source && gz_binary_roundtrip.size() == binary_source.size(), "text=" + gz_roundtrip + ", binary bytes=" + std::to_string((u64)gz_binary_roundtrip.size()));
|
||||
|
||||
bool bad_gz_rejected = false;
|
||||
try
|
||||
{
|
||||
gz_uncompress("not gzip data");
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
bad_gz_rejected = contains(e.what(), "gz_uncompress");
|
||||
}
|
||||
// Invalid gzip input fails host-side; the membrane returns an empty result
|
||||
// rather than throwing into the unit.
|
||||
bool bad_gz_rejected = gz_uncompress("not gzip data") == "";
|
||||
check("gz_uncompress() rejects invalid data", bad_gz_rejected, "invalid stream rejected");
|
||||
|
||||
site_tests_summary(passed, failed, skipped, "ZIP tests write only under /tmp/uce-site-tests-zip.");
|
||||
|
||||
Reference in New Issue
Block a user