diff --git a/docs/wasm-runtime-architecture.md b/docs/wasm-runtime-architecture.md index 359fb74..a1f6053 100644 --- a/docs/wasm-runtime-architecture.md +++ b/docs/wasm-runtime-architecture.md @@ -281,9 +281,12 @@ then checked again before execution. The second check belongs only to that state-changing branch; repeating it immediately after a successful warm check adds no freshness guarantee. -The request context remains UCEB2 so application `context.call` semantics do -not fork into a private transport type. Validated decode trees move into the -request rather than being deep-copied twice; the historical by-value +The internal request envelope carries application `context.call` as UCEB2, +each request `StringMap` as a flat UCEB2 map, scalar session/input metadata as +bounded byte segments, and optional WebSocket state as UCEB2. The guest +validates the complete envelope before moving those values into a fresh +`Request`; transport-only params, cookies, session data, and entry metadata no +longer become duplicate `context.call` children. The historical by-value `DValue::operator=(DValue)` symbol remains exported so warmed side-module artifacts stay ABI-compatible. `request_perf()` subdivides birth into policy, import materialization, core instantiation, export/table lookup, and initialization, diff --git a/site/tests/core.uce b/site/tests/core.uce index 2385080..89d103c 100644 --- a/site/tests/core.uce +++ b/site/tests/core.uce @@ -51,6 +51,7 @@ RENDER(Request& context) check("str_ends_with()", str_ends_with("component.uce", ".uce"), "component.uce ends with .uce"); check("to_lower() / to_upper()", to_lower("MiXeD") == "mixed" && to_upper("MiXeD") == "MIXED", to_lower("MiXeD") + " / " + to_upper("MiXeD")); check("request context params", context.params["SCRIPT_URL"] != "" && context.params["BASE_URL"] != "" && context.params["ROUTE_PATH"] != "" && context.params["ROUTE_PAGE"] != "" && context.params["ROUTE_VALID"] == "1", "script=" + context.params["SCRIPT_URL"] + " base=" + context.params["BASE_URL"] + " route=" + context.params["ROUTE_PATH"] + " page=" + context.params["ROUTE_PAGE"] + " valid=" + context.params["ROUTE_VALID"]); + check("request transport stays out of context.call", !context.call.has("params") && !context.call.has("get") && !context.call.has("post") && !context.call.has("cookies") && !context.call.has("session") && !context.call.has("entry_unit") && !context.call.has("in"), json_encode(context.call)); check("request server configuration", context.server && context.server->config["BIN_DIRECTORY"] != "" && context.server->config["SITE_DIRECTORY"] != "" && context.server->config["SESSION_PATH"] != "" && context.server->config["WASM_CORE_PATH"] != "", "bin=" + context.server->config["BIN_DIRECTORY"] + " site=" + context.server->config["SITE_DIRECTORY"] + " session=" + context.server->config["SESSION_PATH"] + " core=" + context.server->config["WASM_CORE_PATH"]); String saved_query_string = context.params["QUERY_STRING"]; context.params["QUERY_STRING"] = "workspace/projects&theme=dark"; @@ -141,6 +142,11 @@ RENDER(Request& context) DValue uceb_decoded; bool uceb_ok = ucb_decode(uceb_encoded, uceb_decoded, &uceb_error); check("UCEB2 DValue codec round-trip preserves scalar types", uceb_ok && uceb_decoded["name"].to_string() == "phase1" && uceb_decoded["name"].get_type_name() == "String" && uceb_decoded["nested"]["answer"].to_string() == "42" && uceb_decoded["float"].get_type_name() == "f64" && uceb_decoded["float"].to_f64() > 0.00000009 && uceb_decoded["float"].to_f64() < 0.00000011 && uceb_decoded["bool"].get_type_name() == "bool" && uceb_decoded["bool"].to_bool() && uceb_decoded["items"].is_list() && uceb_decoded["items"]["1"].to_string() == "second" && uceb_decoded["empty_list"].is_list() && uceb_decoded["binary"].to_string().size() == uceb_binary.size() && uceb_decoded["binary"].to_string() == uceb_binary && uceb_decoded["pointer"].to_string() == "", "bytes=" + std::to_string((u64)uceb_encoded.size()) + " error=" + uceb_error + " float=" + uceb_decoded["float"].to_string() + " types=" + uceb_decoded["name"].get_type_name() + "/" + uceb_decoded["float"].get_type_name() + "/" + uceb_decoded["bool"].get_type_name()); + StringMap uceb_flat_source = {{"alpha", "one"}, {"binary", uceb_binary}, {"empty", ""}}; + DValue uceb_flat_decoded; + String uceb_flat_error; + bool uceb_flat_ok = ucb_decode(ucb_encode_flat_string_map(uceb_flat_source), uceb_flat_decoded, &uceb_flat_error); + check("UCEB2 flat string-map codec", uceb_flat_ok && uceb_flat_decoded.to_stringmap() == uceb_flat_source, uceb_flat_error + " / " + json_encode(uceb_flat_decoded)); DValue uceb_invalid; String uceb_bad_magic = "NOPE"; diff --git a/src/lib/dvalue.cpp b/src/lib/dvalue.cpp index aeed17d..89db230 100644 --- a/src/lib/dvalue.cpp +++ b/src/lib/dvalue.cpp @@ -921,11 +921,11 @@ bool ucb_append_varint(String& out, u64 value) return(true); } -bool ucb_read_varint(const String& src, size_t& offset, u64& value_out) +bool ucb_read_varint(const char* src, size_t src_size, size_t& offset, u64& value_out) { value_out = 0; u32 shift = 0; - while(offset < src.size() && shift <= 63) + while(offset < src_size && shift <= 63) { u8 byte = (u8)src[offset++]; value_out |= ((u64)(byte & 0x7f) << shift); @@ -936,6 +936,11 @@ bool ucb_read_varint(const String& src, size_t& offset, u64& value_out) return(false); } +bool ucb_read_varint(const String& src, size_t& offset, u64& value_out) +{ + return(ucb_read_varint(src.data(), src.size(), offset, value_out)); +} + char ucb_node_type(const DValue& value) { const DValue& target = value.deref(); @@ -1149,6 +1154,30 @@ String ucb_encode(const DValue& value) return(out); } +// Internal request/config transport uses the same UCEB2 map representation +// without first building a duplicate DValue tree. +String ucb_encode_flat_string_map(const StringMap& value) +{ + String out; + out.append(UCEB_MAGIC, 4); + out.push_back((char)UCEB_VERSION); + out.push_back(0); + out.push_back('M'); + ucb_append_varint(out, 0); + ucb_append_varint(out, value.size()); + for(auto& child : value) + { + ucb_append_varint(out, child.first.size()); + out.append(child.first.data(), child.first.size()); + out.push_back(0); + out.push_back('S'); + ucb_append_varint(out, child.second.size()); + out.append(child.second.data(), child.second.size()); + ucb_append_varint(out, 0); + } + return(out); +} + bool ucb_decode(const String& encoded, DValue& out, String* error_out) { String error; @@ -1176,7 +1205,7 @@ bool ucb_decode(const String& encoded, DValue& out, String* error_out) } #ifdef __UCE_WASM_CORE__ -static bool ucb_decode_flat_string_map(const String& encoded, StringMap& out, String* error_out) +static bool ucb_decode_flat_string_map(const char* encoded, size_t encoded_size, StringMap& out, String* error_out) { String error; StringMap decoded; @@ -1185,14 +1214,14 @@ static bool ucb_decode_flat_string_map(const String& encoded, StringMap& out, St error = message; return(false); }; - if(encoded.size() < 5 || encoded.compare(0, 4, UCEB_MAGIC) != 0) + if(encoded_size < 5 || memcmp(encoded, UCEB_MAGIC, 4) != 0) fail("missing UCEB magic header"); else if((u8)encoded[4] != UCEB_VERSION) fail("unsupported UCEB version"); else { offset = 5; - if(encoded.size() - offset < 2) + if(encoded_size - offset < 2) fail("unexpected end of UCEB2 string map"); else { @@ -1201,44 +1230,44 @@ static bool ucb_decode_flat_string_map(const String& encoded, StringMap& out, St u64 scalar_len = 0, child_count = 0; if(flags != 0 || type != 'M') fail("UCEB2 string map root must be a map"); - else if(!ucb_read_varint(encoded, offset, scalar_len) || scalar_len != 0) + else if(!ucb_read_varint(encoded, encoded_size, offset, scalar_len) || scalar_len != 0) fail("UCEB2 string map root must have no scalar"); - else if(!ucb_read_varint(encoded, offset, child_count)) + else if(!ucb_read_varint(encoded, encoded_size, offset, child_count)) fail("invalid UCEB2 string map child count"); else { for(u64 i = 0; error == "" && i < child_count; i++) { u64 key_len = 0, value_len = 0, value_children = 0; - if(!ucb_read_varint(encoded, offset, key_len) || key_len > encoded.size() - offset) + if(!ucb_read_varint(encoded, encoded_size, offset, key_len) || key_len > encoded_size - offset) { fail("invalid UCEB2 string map key"); break; } - String key(encoded.data() + offset, (size_t)key_len); + String key(encoded + offset, (size_t)key_len); offset += (size_t)key_len; - if(encoded.size() - offset < 2) + if(encoded_size - offset < 2) { fail("unexpected end of UCEB2 string map value"); break; } u8 value_flags = (u8)encoded[offset++]; char value_type = encoded[offset++]; - if(value_flags != 0 || value_type != 'S' || !ucb_read_varint(encoded, offset, value_len) || value_len > encoded.size() - offset) + if(value_flags != 0 || value_type != 'S' || !ucb_read_varint(encoded, encoded_size, offset, value_len) || value_len > encoded_size - offset) { fail("UCEB2 string map value must be a string scalar"); break; } - String value(encoded.data() + offset, (size_t)value_len); + String value(encoded + offset, (size_t)value_len); offset += (size_t)value_len; - if(!ucb_read_varint(encoded, offset, value_children) || value_children != 0) + if(!ucb_read_varint(encoded, encoded_size, offset, value_children) || value_children != 0) { fail("UCEB2 string map value cannot have children"); break; } decoded[std::move(key)] = std::move(value); } - if(error == "" && offset != encoded.size()) + if(error == "" && offset != encoded_size) fail("trailing bytes after UCEB2 string map"); } } diff --git a/src/lib/dvalue.h b/src/lib/dvalue.h index 98c544e..bcb6336 100644 --- a/src/lib/dvalue.h +++ b/src/lib/dvalue.h @@ -78,6 +78,7 @@ String to_String(DValue t); String var_dump(const DValue& map, String prefix = "", String postfix = "\n"); String ucb_encode(const DValue& value); +String ucb_encode_flat_string_map(const StringMap& value); DValue ucb_decode(const String& encoded); bool ucb_decode(const String& encoded, DValue& out, String* error_out = 0); diff --git a/src/wasm/backend.cpp b/src/wasm/backend.cpp index e638eb8..4510068 100644 --- a/src/wasm/backend.cpp +++ b/src/wasm/backend.cpp @@ -154,47 +154,7 @@ bool wasm_backend_should_handle(Request& request, const String& entry_unit) // configured error page. String wasm_backend_serve(Request& request, const String& entry_unit, const String& handler = "render") { - DValue ctx; - auto copy_map = [&](const StringMap& source, const char* key) { - for(auto& entry : source) - ctx[key][entry.first] = entry.second; - }; - copy_map(request.params, "params"); - copy_map(request.get, "get"); - copy_map(request.post, "post"); - copy_map(request.cookies, "cookies"); - copy_map(request.session, "session"); - ctx["session_id"] = request.session_id; - ctx["session_name"] = request.session_name; - ctx["session_loaded_hash"] = request.session_loaded_hash; - ctx["entry_unit"] = entry_unit; - // Raw request body: cli_input() parses a JSON CLI payload from context.in, - // and serve_http handlers read it as req->in; carried into the workspace. - ctx["in"] = request.in; - // Configurable error pages read context.call["error"] (the error_info DValue - // set natively in render_wasm_error_page). apply_context binds context.call to - // the decoded ctx, so marshal the native request.call children across the - // membrane — otherwise the error page renders with no error data. - for(auto& entry : request.call._map) - ctx[entry.first] = entry.second; - // WebSocket event context: the workspace owns no connections, so the frame's - // connection identity goes in and the handler's ws_send/ws_close dispatch - // commands come back out (below) for the native broker to apply. - if(handler == "websocket") - { - ctx["ws"]["connection_id"] = request.resources.websocket_connection_id; - ctx["ws"]["scope"] = request.resources.websocket_scope; - ctx["ws"]["opcode"] = (f64)request.resources.websocket_opcode; - ctx["ws"]["binary"].set_bool(request.resources.websocket_is_binary); - for(auto& id : request.resources.websocket_scope_connection_ids) - { - DValue v; v = id; - ctx["ws"]["connections"].push(v); - } - ctx["ws"]["connection_state"] = request.connection; - } - - WasmResponse response = wasm_worker_serve(*g_wasm_worker, ctx, entry_unit, handler, &request); + WasmResponse response = wasm_worker_serve(*g_wasm_worker, request, entry_unit, handler); request.stats.wasm_dispatch_us = response.dispatch_us; request.stats.wasm_workspace_complete_us = response.workspace_complete_us; request.stats.wasm_entry_invoke_us = response.entry_invoke_us; diff --git a/src/wasm/core.cpp b/src/wasm/core.cpp index 2936202..62e4687 100644 --- a/src/wasm/core.cpp +++ b/src/wasm/core.cpp @@ -723,6 +723,46 @@ void unit_render(String file_name, Request& request) void unit_render(String file_name) { unit_render(file_name, *context); } +struct WasmRequestEnvelopeSegment +{ + const char* data = 0; + size_t size = 0; +}; + +static bool wasm_decode_request_envelope(const char* encoded, size_t encoded_size, + WasmRequestEnvelopeSegment (&segments)[12], String& error) +{ + if(encoded_size < 6 || memcmp(encoded, "UCER", 4) != 0) + { + error = "missing UCE request-envelope header"; + return(false); + } + if((u8)encoded[4] != 1 || (u8)encoded[5] != 12) + { + error = "unsupported UCE request-envelope version or segment count"; + return(false); + } + size_t offset = 6; + for(u32 i = 0; i < 12; i++) + { + u64 segment_size = 0; + if(!ucb_read_varint(encoded, encoded_size, offset, segment_size) || segment_size > encoded_size - offset) + { + error = "invalid UCE request-envelope segment " + std::to_string(i); + return(false); + } + segments[i].data = encoded + offset; + segments[i].size = (size_t)segment_size; + offset += (size_t)segment_size; + } + if(offset != encoded_size) + { + error = "trailing bytes after UCE request envelope"; + return(false); + } + return(true); +} + extern "C" { // The host has already loaded the request's entry unit and can place its @@ -807,46 +847,66 @@ int uce_wasm_apply_context(const char* config_buf, size_t config_len, const char if(context == 0) uce_wasm_core_init(); StringMap decoded_config; - DValue decoded; + StringMap decoded_params; + StringMap decoded_get; + StringMap decoded_post; + StringMap decoded_cookies; + StringMap decoded_session; + DValue decoded_call; + DValue decoded_ws; + WasmRequestEnvelopeSegment segments[12]; String error; - if(!ucb_decode_flat_string_map(String(config_buf, config_len), decoded_config, &error)) + if(!ucb_decode_flat_string_map(config_buf, config_len, decoded_config, &error)) { uce_host_log(3, error.data(), error.size()); return(1); } - if(!ucb_decode(String(context_buf, context_len), decoded, &error)) + if(!wasm_decode_request_envelope(context_buf, context_len, segments, error)) { uce_host_log(3, error.data(), error.size()); return(2); } - wasm_server.config = std::move(decoded_config); - wasm_request.call = std::move(decoded); - DValue& applied = wasm_request.call; - auto apply_map = [](DValue* source, StringMap& dest) { - dest.clear(); - if(source) - source->each([&](const DValue& item, String key) { - dest[key] = item.to_string(); - }); + auto decode_tree = [&](u32 index, DValue& target, const char* name) { + if(ucb_decode(String(segments[index].data, segments[index].size), target, &error)) + return(true); + error = String(name) + ": " + error; + return(false); }; - apply_map(applied.key("params"), wasm_request.params); + auto decode_map = [&](u32 index, StringMap& target, const char* name) { + if(ucb_decode_flat_string_map(segments[index].data, segments[index].size, target, &error)) + return(true); + error = String(name) + ": " + error; + return(false); + }; + if(!decode_tree(0, decoded_call, "request call") || + !decode_map(1, decoded_params, "request params") || + !decode_map(2, decoded_get, "request get") || + !decode_map(3, decoded_post, "request post") || + !decode_map(4, decoded_cookies, "request cookies") || + !decode_map(5, decoded_session, "request session")) + { + uce_host_log(3, error.data(), error.size()); + return(3); + } + if(segments[11].size && !decode_tree(11, decoded_ws, "request websocket")) + { + uce_host_log(3, error.data(), error.size()); + return(4); + } + wasm_server.config = std::move(decoded_config); + wasm_request.call = std::move(decoded_call); + wasm_request.params = std::move(decoded_params); + wasm_request.get = std::move(decoded_get); + wasm_request.post = std::move(decoded_post); + wasm_request.cookies = std::move(decoded_cookies); + wasm_request.session = std::move(decoded_session); wasm_request.response_code = wasm_request.params["GATEWAY_INTERFACE"] != "" ? "Status: 200 OK" : "HTTP/1.1 200 OK"; - apply_map(applied.key("get"), wasm_request.get); - apply_map(applied.key("post"), wasm_request.post); - apply_map(applied.key("cookies"), wasm_request.cookies); - apply_map(applied.key("session"), wasm_request.session); - if(DValue* session_id = applied.key("session_id")) - wasm_request.session_id = session_id->to_string(); - if(DValue* session_name = applied.key("session_name")) - wasm_request.session_name = session_name->to_string(); - if(DValue* session_loaded_hash = applied.key("session_loaded_hash")) - wasm_request.session_loaded_hash = session_loaded_hash->to_string(); - DValue* entry = applied.key("entry_unit"); - if(entry) - wasm_request.resources.current_unit_file = entry->to_string(); - DValue* raw_in = applied.key("in"); - wasm_request.in = raw_in ? raw_in->to_string() : ""; + wasm_request.session_id.assign(segments[6].data, segments[6].size); + wasm_request.session_name.assign(segments[7].data, segments[7].size); + wasm_request.session_loaded_hash.assign(segments[8].data, segments[8].size); + wasm_request.resources.current_unit_file.assign(segments[9].data, segments[9].size); + wasm_request.in.assign(segments[10].data, segments[10].size); // websocket event context: ws_send()/ws_close() capture into the dispatch // list (the workspace owns no connections), which collect() carries back to // the broker. Reset per invocation. @@ -854,20 +914,19 @@ int uce_wasm_apply_context(const char* config_buf, size_t config_len, const char wasm_request.resources.websocket_connection_state_before = DValue(); wasm_request.resources.websocket_dispatch_commands = DValue(); wasm_request.resources.websocket_dispatch_capture = false; - DValue* ws = applied.key("ws"); - if(ws) + if(segments[11].size) { - wasm_request.resources.websocket_connection_id = (*ws)["connection_id"].to_string(); - wasm_request.resources.websocket_scope = (*ws)["scope"].to_string(); - wasm_request.resources.websocket_opcode = (u8)(*ws)["opcode"].to_u64(); - wasm_request.resources.websocket_is_binary = (*ws)["binary"].to_bool(); + wasm_request.resources.websocket_connection_id = decoded_ws["connection_id"].to_string(); + wasm_request.resources.websocket_scope = decoded_ws["scope"].to_string(); + wasm_request.resources.websocket_opcode = (u8)decoded_ws["opcode"].to_u64(); + wasm_request.resources.websocket_is_binary = decoded_ws["binary"].to_bool(); wasm_request.resources.websocket_scope_connection_ids.clear(); - if(DValue* conns = ws->key("connections")) + if(DValue* conns = decoded_ws.key("connections")) conns->each([&](const DValue& v, String) { wasm_request.resources.websocket_scope_connection_ids.push_back(v.to_string()); }); wasm_request.resources.websocket_dispatch_capture = true; - if(DValue* cstate = ws->key("connection_state")) + if(DValue* cstate = decoded_ws.key("connection_state")) wasm_request.connection = *cstate; wasm_request.resources.websocket_connection_state_before = wasm_request.connection; } diff --git a/src/wasm/worker.cpp b/src/wasm/worker.cpp index 5a8d72c..99e9c99 100644 --- a/src/wasm/worker.cpp +++ b/src/wasm/worker.cpp @@ -1266,6 +1266,58 @@ private: std::vector hostcall_operation_names; }; +// The request envelope keeps app scratch state separate from transport fields. +// Maps use UCEB2 directly so the guest can populate Request StringMaps without +// constructing and then copying a duplicate DValue subtree. +static void wasm_request_envelope_append(String& envelope, const String& value) +{ + u64 size = value.size(); + while(size >= 0x80) + { + envelope.push_back((char)((size & 0x7f) | 0x80)); + size >>= 7; + } + envelope.push_back((char)size); + envelope.append(value.data(), value.size()); +} + +static String wasm_encode_request_envelope(const Request& request, const String& entry_unit, const String& handler) +{ + String envelope = "UCER"; + envelope.push_back(1); + envelope.push_back(12); + wasm_request_envelope_append(envelope, ucb_encode(request.call)); + wasm_request_envelope_append(envelope, ucb_encode_flat_string_map(request.params)); + wasm_request_envelope_append(envelope, ucb_encode_flat_string_map(request.get)); + wasm_request_envelope_append(envelope, ucb_encode_flat_string_map(request.post)); + wasm_request_envelope_append(envelope, ucb_encode_flat_string_map(request.cookies)); + wasm_request_envelope_append(envelope, ucb_encode_flat_string_map(request.session)); + wasm_request_envelope_append(envelope, request.session_id); + wasm_request_envelope_append(envelope, request.session_name); + wasm_request_envelope_append(envelope, request.session_loaded_hash); + wasm_request_envelope_append(envelope, entry_unit); + wasm_request_envelope_append(envelope, request.in); + String websocket; + if(handler == "websocket") + { + DValue ws; + ws["connection_id"] = request.resources.websocket_connection_id; + ws["scope"] = request.resources.websocket_scope; + ws["opcode"] = (f64)request.resources.websocket_opcode; + ws["binary"].set_bool(request.resources.websocket_is_binary); + for(auto& id : request.resources.websocket_scope_connection_ids) + { + DValue value; + value = id; + ws["connections"].push(value); + } + ws["connection_state"] = request.connection; + websocket = ucb_encode(ws); + } + wasm_request_envelope_append(envelope, websocket); + return(envelope); +} + // ---- workspace (per request) ---------------------------------------------- class WasmWorkspace : public WasmRequestProfile @@ -1524,7 +1576,7 @@ public: return(""); } - String apply_context(const DValue& context_tree) + String apply_context(const Request& request, const String& entry_unit, const String& handler) { auto phase_start = std::chrono::steady_clock::now(); auto phase_us = [&]() -> u64 { @@ -1533,7 +1585,7 @@ public: phase_start = now; return(elapsed); }; - String encoded = ucb_encode(context_tree); + String encoded = wasm_encode_request_envelope(request, entry_unit, handler); server_config_bytes = worker.server_config_encoded.size(); context_bytes = server_config_bytes + encoded.size(); context_encode_us = phase_us(); @@ -3787,15 +3839,15 @@ inline String wasm_worker_prepare(WasmWorker& worker) // ---- public entry: one request through one workspace ----------------------- -inline WasmResponse wasm_worker_serve(WasmWorker& worker, const DValue& context_tree, const String& entry_source_path, - const String& handler = "render", const Request* request = 0) +inline WasmResponse wasm_worker_serve(WasmWorker& worker, const Request& request, const String& entry_source_path, + const String& handler = "render") { WasmResponse response; f64 serve_started = time_precise(); auto workspace_start = std::chrono::steady_clock::now(); f64 cpu_started = wasm_thread_cpu_time(); struct rusage thread_runtime_start = {}; - bool thread_runtime_profiled = request && worker.cfg.profile_thread_runtime && getrusage(RUSAGE_THREAD, &thread_runtime_start) == 0; + bool thread_runtime_profiled = worker.cfg.profile_thread_runtime && getrusage(RUSAGE_THREAD, &thread_runtime_start) == 0; int thread_cpu_start = thread_runtime_profiled ? sched_getcpu() : -1; WasmWorkspace workspace(worker); f64 setup_cpu_finished = wasm_thread_cpu_time(); @@ -3803,17 +3855,14 @@ inline WasmResponse wasm_worker_serve(WasmWorker& worker, const DValue& context_ std::chrono::steady_clock::now() - workspace_start).count(); workspace.workspace_setup_cpu_us = cpu_started > 0 && setup_cpu_finished > cpu_started ? (u64)((setup_cpu_finished - cpu_started) * 1000000.0) : 0; - if(request) - { - workspace.set_perf_snapshot(my_pid, (u64)parent_pid, request->server ? request->server->request_count : 0, - request->stats.time_init, request->stats.time_params, request->stats.time_input, request->stats.time_start, - serve_started, cpu_started); - workspace.request_perf.thread_runtime_start = thread_runtime_start; - workspace.request_perf.thread_cpu_start = thread_cpu_start; - workspace.request_perf.thread_runtime_profiled = thread_runtime_profiled; - if(request->stats.time_start > 0 && serve_started > request->stats.time_start) - workspace.dispatch_us = (u64)((serve_started - request->stats.time_start) * 1000000.0); - } + workspace.set_perf_snapshot(my_pid, (u64)parent_pid, request.server ? request.server->request_count : 0, + request.stats.time_init, request.stats.time_params, request.stats.time_input, request.stats.time_start, + serve_started, cpu_started); + workspace.request_perf.thread_runtime_start = thread_runtime_start; + workspace.request_perf.thread_cpu_start = thread_cpu_start; + workspace.request_perf.thread_runtime_profiled = thread_runtime_profiled; + if(request.stats.time_start > 0 && serve_started > request.stats.time_start) + workspace.dispatch_us = (u64)((serve_started - request.stats.time_start) * 1000000.0); auto birth_start = std::chrono::steady_clock::now(); f64 birth_cpu_start = wasm_thread_cpu_time(); String error = workspace.birth(); @@ -3826,7 +3875,7 @@ inline WasmResponse wasm_worker_serve(WasmWorker& worker, const DValue& context_ { auto context_start = std::chrono::steady_clock::now(); f64 context_cpu_start = wasm_thread_cpu_time(); - error = workspace.apply_context(context_tree); + error = workspace.apply_context(request, entry_source_path, handler); f64 context_cpu_finished = wasm_thread_cpu_time(); workspace.context_apply_us = (u64)std::chrono::duration_cast( std::chrono::steady_clock::now() - context_start).count();