chore: narrow wasm backend entrypoint API
This commit is contained in:
@@ -958,10 +958,10 @@ int handle_complete(FastCGIRequest& request) {
|
|||||||
// wasm invocation: Wasmtime uses host signals internally to implement
|
// wasm invocation: Wasmtime uses host signals internally to implement
|
||||||
// guest traps, and the native handler would otherwise turn a clean
|
// guest traps, and the native handler would otherwise turn a clean
|
||||||
// guest trap into a native fatal signal. Sets failure_* on error.
|
// guest trap into a native fatal signal. Sets failure_* on error.
|
||||||
auto serve_via_wasm = [&](const String& entry_unit, int32_t kind, const String& handler_name = "") {
|
auto serve_via_wasm = [&](const String& entry_unit, int32_t kind) {
|
||||||
request_fault_active = 0;
|
request_fault_active = 0;
|
||||||
restore_request_fault_handlers();
|
restore_request_fault_handlers();
|
||||||
String wasm_error = wasm_backend_serve(request, entry_unit, kind, handler_name);
|
String wasm_error = wasm_backend_serve(request, entry_unit, kind);
|
||||||
install_request_fault_handlers();
|
install_request_fault_handlers();
|
||||||
request_fault_active = 1;
|
request_fault_active = 1;
|
||||||
if(wasm_error != "")
|
if(wasm_error != "")
|
||||||
@@ -1251,10 +1251,6 @@ void custom_server_http_dispatcher_loop(String key)
|
|||||||
custom_server_dispatcher_key = key;
|
custom_server_dispatcher_key = key;
|
||||||
close_inherited_server_sockets();
|
close_inherited_server_sockets();
|
||||||
install_process_fault_handlers();
|
install_process_fault_handlers();
|
||||||
// This broker is forked from a worker that may already hold a wasm engine;
|
|
||||||
// drop the inherited (unusable post-fork) engine so it re-inits its own.
|
|
||||||
wasm_backend_reset_after_fork();
|
|
||||||
|
|
||||||
StringMap cfg = custom_server_read_config(key);
|
StringMap cfg = custom_server_read_config(key);
|
||||||
if(cfg["type"] != "http" || cfg["bind"] == "")
|
if(cfg["type"] != "http" || cfg["bind"] == "")
|
||||||
{
|
{
|
||||||
|
|||||||
+9
-30
@@ -168,12 +168,11 @@ bool wasm_backend_should_handle(Request& request, const String& entry_unit)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Serve a request through a wasm workspace using the unit handler selected by
|
// Serve a request through a wasm workspace using the unit handler selected by
|
||||||
// `kind` (page render / cli / serve_http). Populates the native Request
|
// `kind` (page render or cli). Populates the native Request
|
||||||
// (status/headers/cookies/session/body) so the existing transport writes the
|
// (status/headers/cookies/session/body) so the existing transport writes the
|
||||||
// response unchanged. Returns "" on success, or a collapsed error/trace string
|
// response unchanged. Returns "" on success, or a collapsed error/trace string
|
||||||
// for the caller to route into the configured error page.
|
// for the caller to route into the configured error page.
|
||||||
String wasm_backend_serve(Request& request, const String& entry_unit, int32_t kind = WasmWorkspace::RESOLVE_RENDER,
|
String wasm_backend_serve(Request& request, const String& entry_unit, int32_t kind = WasmWorkspace::RESOLVE_RENDER)
|
||||||
const String& handler_name = "")
|
|
||||||
{
|
{
|
||||||
DValue ctx;
|
DValue ctx;
|
||||||
auto copy_map = [&](const StringMap& source, const char* key) {
|
auto copy_map = [&](const StringMap& source, const char* key) {
|
||||||
@@ -187,20 +186,19 @@ String wasm_backend_serve(Request& request, const String& entry_unit, int32_t ki
|
|||||||
copy_map(request.session, "session");
|
copy_map(request.session, "session");
|
||||||
ctx["entry_unit"] = entry_unit;
|
ctx["entry_unit"] = entry_unit;
|
||||||
// Raw request body: cli_input() parses a JSON CLI payload from context.in,
|
// Raw request body: cli_input() parses a JSON CLI payload from context.in,
|
||||||
// and ws_message() exposes the websocket frame from it — both need it carried
|
// carried into the workspace, not just the form-decoded post map.
|
||||||
// into the workspace, not just the form-decoded post map.
|
|
||||||
ctx["in"] = request.in;
|
ctx["in"] = request.in;
|
||||||
|
|
||||||
WasmResponse response = wasm_worker_serve(*g_wasm_worker, ctx, entry_unit, kind, handler_name);
|
WasmResponse response = wasm_worker_serve(*g_wasm_worker, ctx, entry_unit, kind);
|
||||||
if(!response.ok)
|
if(!response.ok)
|
||||||
return(response.error == "" ? String("wasm workspace failed") : response.error);
|
return(response.error == "" ? String("wasm workspace failed") : response.error);
|
||||||
|
|
||||||
// A cli/serve_http unit that does not export the requested handler is a 404,
|
// A cli unit that does not export __uce_cli is a 404, matching native
|
||||||
// matching native compiler_invoke_cli ("CLI Entry Point Not Found"). For page
|
// compiler_invoke_cli ("CLI Entry Point Not Found"). For page render a
|
||||||
// render a missing handler is simply an empty body (native parity).
|
// missing handler is simply an empty body (native parity).
|
||||||
if(!response.handler_present && kind != WasmWorkspace::RESOLVE_RENDER)
|
if(!response.handler_present && kind == WasmWorkspace::RESOLVE_CLI)
|
||||||
{
|
{
|
||||||
request.set_status(404, kind == WasmWorkspace::RESOLVE_CLI ? "CLI Entry Point Not Found" : "Handler Not Found");
|
request.set_status(404, "CLI Entry Point Not Found");
|
||||||
return("");
|
return("");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,22 +256,3 @@ void wasm_backend_shutdown()
|
|||||||
g_wasm_epoch_ticker = 0;
|
g_wasm_epoch_ticker = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A connection-broker child (the custom-server HTTP dispatcher, the websocket
|
|
||||||
// exec child) is forked from a worker that may already have brought up its wasm
|
|
||||||
// engine. The Wasmtime engine and the epoch-ticker thread must not cross fork:
|
|
||||||
// the child inherits the pointer/flags but only the forking thread survives, so
|
|
||||||
// the ticker is a phantom and the engine state is unsafe. Reset the per-process
|
|
||||||
// statics so the child lazily initializes its own engine + ticker on first use.
|
|
||||||
// The inherited std::thread refers to a thread that does not exist in the child;
|
|
||||||
// discard the inherited pointer without touching the pointed-to object. Joining,
|
|
||||||
// deleting, or destroying a joinable std::thread after fork can terminate the
|
|
||||||
// child; the child will allocate its own ticker on first wasm use.
|
|
||||||
void wasm_backend_reset_after_fork()
|
|
||||||
{
|
|
||||||
g_wasm_worker = 0;
|
|
||||||
g_wasm_init_attempted = false;
|
|
||||||
g_wasm_init_error = "";
|
|
||||||
g_wasm_epoch_running.store(false);
|
|
||||||
g_wasm_epoch_ticker = 0;
|
|
||||||
}
|
|
||||||
|
|||||||
+4
-9
@@ -9,14 +9,13 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
// Resolve-kind selector for wasm_backend_serve, mirrored from
|
// Resolve-kind selector for wasm_backend_serve, mirrored from
|
||||||
// WasmWorkspace::ResolveKind in worker.cpp. Kept in sync there; the values are
|
// WasmWorkspace::ResolveKind in worker.cpp. Only the entry kinds actually wired
|
||||||
// the wasm unit-handler dispatch kinds the host loader understands.
|
// to the wasm backend live here; WebSocket/serve_http re-enter when W7b/W7c land
|
||||||
|
// (via the broker→worker-pool forwarding model, not in-fork rendering).
|
||||||
namespace wasm_kind {
|
namespace wasm_kind {
|
||||||
enum {
|
enum {
|
||||||
RENDER = 1,
|
RENDER = 1,
|
||||||
CLI = 4,
|
CLI = 4,
|
||||||
WEBSOCKET = 5,
|
|
||||||
SERVE_HTTP = 6,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,11 +26,7 @@ bool wasm_backend_should_handle(Request& request, const String& entry_unit);
|
|||||||
// Serve a request through a wasm workspace using the unit handler for `kind`,
|
// Serve a request through a wasm workspace using the unit handler for `kind`,
|
||||||
// populating the native Request. Returns "" on success or a collapsed error.
|
// populating the native Request. Returns "" on success or a collapsed error.
|
||||||
String wasm_backend_serve(Request& request, const String& entry_unit,
|
String wasm_backend_serve(Request& request, const String& entry_unit,
|
||||||
int32_t kind = wasm_kind::RENDER, const String& handler_name = "");
|
int32_t kind = wasm_kind::RENDER);
|
||||||
|
|
||||||
// Join the per-process epoch ticker before the worker process exits.
|
// Join the per-process epoch ticker before the worker process exits.
|
||||||
void wasm_backend_shutdown();
|
void wasm_backend_shutdown();
|
||||||
|
|
||||||
// Drop the inherited (post-fork unusable) engine so a broker child re-inits its
|
|
||||||
// own; call right after fork in a long-lived child.
|
|
||||||
void wasm_backend_reset_after_fork();
|
|
||||||
|
|||||||
+7
-18
@@ -487,43 +487,32 @@ public:
|
|||||||
// provided, reports whether the unit actually exports that handler — the
|
// provided, reports whether the unit actually exports that handler — the
|
||||||
// caller maps a missing render to an empty body (native parity) and a
|
// caller maps a missing render to an empty body (native parity) and a
|
||||||
// missing cli/serve handler to a 404.
|
// missing cli/serve handler to a 404.
|
||||||
String invoke_entry(const String& entry_source_path, int32_t kind, bool* handler_present = 0,
|
String invoke_entry(const String& entry_source_path, int32_t kind, bool* handler_present = 0)
|
||||||
const String& handler_name = "")
|
|
||||||
{
|
{
|
||||||
entry_dir = dir_of(entry_source_path);
|
entry_dir = dir_of(entry_source_path);
|
||||||
size_t unit_index = 0;
|
size_t unit_index = 0;
|
||||||
String error = load_unit(entry_source_path, unit_index);
|
String error = load_unit(entry_source_path, unit_index);
|
||||||
if(error != "")
|
if(error != "")
|
||||||
return(error);
|
return(error);
|
||||||
// Base handler export for the kind, plus the named suffix (serve_http /
|
String symbol = kind == RESOLVE_CLI ? "__uce_cli" : "__uce_render";
|
||||||
// render / component can route to a named entry, e.g. __uce_serve_http_x).
|
|
||||||
String symbol = kind == RESOLVE_CLI ? "__uce_cli"
|
|
||||||
: kind == RESOLVE_WEBSOCKET ? "__uce_websocket"
|
|
||||||
: kind == RESOLVE_SERVE_HTTP ? "__uce_serve_http"
|
|
||||||
: "__uce_render";
|
|
||||||
if(handler_name != "")
|
|
||||||
symbol += "_" + sanitize_symbol_suffix(handler_name);
|
|
||||||
bool present = (bool)unit_func(unit_index, symbol);
|
bool present = (bool)unit_func(unit_index, symbol);
|
||||||
if(handler_present)
|
if(handler_present)
|
||||||
*handler_present = present;
|
*handler_present = present;
|
||||||
if(!present)
|
if(!present)
|
||||||
return("");
|
return("");
|
||||||
// The core parses a "path:name" target into the named suffix; the bare
|
|
||||||
// path above loaded the module, this drives the handler resolution.
|
|
||||||
String target = handler_name == "" ? entry_source_path : entry_source_path + ":" + handler_name;
|
|
||||||
// Invoke through the core so the entry gets the same ONCE() + dispatch
|
// Invoke through the core so the entry gets the same ONCE() + dispatch
|
||||||
// path as components (unit is pre-loaded above; resolution is cached).
|
// path as components (unit is pre-loaded above; resolution is cached).
|
||||||
auto entry = core_func("uce_wasm_invoke_entry");
|
auto entry = core_func("uce_wasm_invoke_entry");
|
||||||
if(!entry)
|
if(!entry)
|
||||||
return("core does not export uce_wasm_invoke_entry");
|
return("core does not export uce_wasm_invoke_entry");
|
||||||
int32_t guest_ptr = 0;
|
int32_t guest_ptr = 0;
|
||||||
error = call_core("uce_alloc", { (int32_t)target.size() }, &guest_ptr);
|
error = call_core("uce_alloc", { (int32_t)entry_source_path.size() }, &guest_ptr);
|
||||||
if(error != "" || guest_ptr == 0)
|
if(error != "" || guest_ptr == 0)
|
||||||
return(error == "" ? String("guest uce_alloc failed for entry path") : error);
|
return(error == "" ? String("guest uce_alloc failed for entry path") : error);
|
||||||
error = guest_write((u32)guest_ptr, target);
|
error = guest_write((u32)guest_ptr, entry_source_path);
|
||||||
if(error != "")
|
if(error != "")
|
||||||
return(error);
|
return(error);
|
||||||
auto result = entry->call(ctx(), { wasmtime::Val(guest_ptr), wasmtime::Val((int32_t)target.size()), wasmtime::Val(kind) });
|
auto result = entry->call(ctx(), { wasmtime::Val(guest_ptr), wasmtime::Val((int32_t)entry_source_path.size()), wasmtime::Val(kind) });
|
||||||
call_core("uce_free", { guest_ptr }, 0);
|
call_core("uce_free", { guest_ptr }, 0);
|
||||||
if(!result)
|
if(!result)
|
||||||
return(trap_text(result.err()));
|
return(trap_text(result.err()));
|
||||||
@@ -1765,7 +1754,7 @@ private:
|
|||||||
// ---- public entry: one request through one workspace -----------------------
|
// ---- public entry: one request through one workspace -----------------------
|
||||||
|
|
||||||
inline WasmResponse wasm_worker_serve(WasmWorker& worker, const DValue& context_tree, const String& entry_source_path,
|
inline WasmResponse wasm_worker_serve(WasmWorker& worker, const DValue& context_tree, const String& entry_source_path,
|
||||||
int32_t kind = WasmWorkspace::RESOLVE_RENDER, const String& handler_name = "")
|
int32_t kind = WasmWorkspace::RESOLVE_RENDER)
|
||||||
{
|
{
|
||||||
WasmResponse response;
|
WasmResponse response;
|
||||||
WasmWorkspace workspace(worker);
|
WasmWorkspace workspace(worker);
|
||||||
@@ -1776,7 +1765,7 @@ inline WasmResponse wasm_worker_serve(WasmWorker& worker, const DValue& context_
|
|||||||
if(error == "")
|
if(error == "")
|
||||||
error = workspace.apply_context(context_tree);
|
error = workspace.apply_context(context_tree);
|
||||||
if(error == "")
|
if(error == "")
|
||||||
error = workspace.invoke_entry(entry_source_path, kind, &response.handler_present, handler_name);
|
error = workspace.invoke_entry(entry_source_path, kind, &response.handler_present);
|
||||||
if(error == "")
|
if(error == "")
|
||||||
error = workspace.collect(response);
|
error = workspace.collect(response);
|
||||||
response.workspace_birth_us = workspace.workspace_birth_us;
|
response.workspace_birth_us = workspace.workspace_birth_us;
|
||||||
|
|||||||
Reference in New Issue
Block a user