Decode request maps directly into Wasm workspaces
This commit is contained in:
parent
516ed94032
commit
5e140d6a5a
@ -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,
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -1266,6 +1266,58 @@ private:
|
||||
std::vector<String> 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::microseconds>(
|
||||
std::chrono::steady_clock::now() - context_start).count();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user