W7 done done
This commit is contained in:
1 parent
b1a0df9c93
commit
f5637bb587
31 files changed
+558
-1148
No files matched your search
@@ -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
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 };
|
||||
|
||||
Executable
+94
@@ -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())
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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
|
||||
@@ -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.
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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?!?
|
||||
|
||||
Reference in new issue
Block a user