cleanup, error pages

This commit is contained in:
root 2026-06-16 08:46:54 +00:00
parent f2a3503ac3
commit dff1959341
6 changed files with 35 additions and 62 deletions

View File

@ -85,3 +85,12 @@ ARCHIVE_MAX_ZIP_ENTRIES=4096
# LIFETIME OF SESSION COOKIES IN SECONDS
SESSION_TIME=2592000
# DEVELOPER-DEFINED ERROR PAGES (see /doc/index.uce?p=error_pages and
# /doc/index.uce?p=3_blocked_functions). The unit receives the error details in
# context.call["error"]; comment out for the plain-text fallback instead.
page_runtime_error=site/errors/runtime-error.uce
# Comma-separated uce_host_* names to disable per server policy (empty = none).
# A unit calling a blocked hostcall fails into the error page above.
UCE_HOSTCALL_BLOCKLIST=

View File

@ -172,6 +172,11 @@ void cli_run_http_smoke()
cli_expect_http("uce_http_smoke:starter workspace ONCE assets reach head", "/examples/uce-starter/?workspace/projects", 200, "css/workspace.css");
cli_expect_http("uce_http_smoke:starter ajax section", "/examples/uce-starter/?page2-section1", 200, "UCE starter AJAX fragment response");
cli_expect_http("uce_http_smoke:starter route traversal blocked", "/examples/uce-starter/?../../../demo/index", 404, "The requested page does not exist.");
// Configurable error page (page_runtime_error): a runtime fault must render the
// developer-defined error-page unit with the real error info marshalled across
// the membrane, at the error status. Asserts HTTP 500 AND that error["request_uri"]
// reached the page (the "mode=trap" query is only shown if error_info crossed).
cli_expect_http("uce_http_smoke:configurable error page 500 + error info", "/demo/error-reporting.uce?mode=trap", 500, "mode=trap");
}
void cli_run_site_suite()

View File

@ -497,35 +497,6 @@ void compiler_record_compile_result(SharedUnit* su, f64 duration, bool success,
}
}
void compiler_begin_render_result(SharedUnit* su, bool count_request)
{
su->invoke_count += 1;
if(count_request)
su->request_count += 1;
su->last_rendered = time();
}
void compiler_record_render_result(SharedUnit* su, f64 duration, bool success, String error_status = "")
{
su->last_render_duration = duration;
su->total_render_duration += duration;
if(su->best_render_duration == 0 || duration < su->best_render_duration)
su->best_render_duration = duration;
if(duration > su->worst_render_duration)
su->worst_render_duration = duration;
if(success)
{
su->runtime_error_status = "";
}
else
{
su->runtime_error_count += 1;
su->runtime_error_status = error_status;
su->last_error = time();
}
}
String compiler_error_status(SharedUnit* su)
{
if(!su)
@ -591,24 +562,6 @@ void compiler_tree_set_bool(DValue& tree, String key, bool value)
tree[key].set_bool(value);
}
bool compiler_is_request_entry_unit(Request* context, SharedUnit* su)
{
if(!context || !su)
return(false);
auto request_file = compiler_normalize_unit_path(context, context->params["SCRIPT_FILENAME"]);
return(request_file != "" && request_file == su->file_name);
}
bool compiler_can_write_response(Request* context)
{
return(
context &&
context->params["REQUEST_METHOD"] != "" &&
context->ob &&
context->ob_stack.size() > 0
);
}
}
String preprocess_shared_unit(Request* context, SharedUnit* su)

View File

@ -625,7 +625,7 @@ String trim(String raw)
s64 len = raw.length();
s64 start_pos = 0;
s64 end_pos = len - 1;
if(len == 0 || (len == 1 && isspace((unsigned char)raw[0])))
if(len == 0)
return("");
while(start_pos < len && isspace((unsigned char)raw[start_pos]))
start_pos++;
@ -2308,7 +2308,7 @@ String json_decode_String(String s, u32& i, char termination_char)
result.append(1, '\'');
break;
case('u'):
if(i <= s.length() && s.length() - i >= 5 && json_decode_unicode_escape(s, i, result))
if(s.length() - i >= 5 && json_decode_unicode_escape(s, i, result))
break;
return(result);
default:

View File

@ -112,6 +112,10 @@ bool render_wasm_error_page(Request& request, String config_key, s32 status_code
request.call.remove("error");
return(false);
}
// Rendering the error-page unit set the response to its own (200) status;
// re-assert the error status + html content-type so the client sees e.g. 500.
request.set_status(status_code, status_reason);
request.header["Content-Type"] = first(request.server->config["CONTENT_TYPE"], "text/html; charset=utf-8");
print(html);
return(true);
}

View File

@ -375,18 +375,25 @@ static bool uce_header_name_safe(String name)
static DValue uce_http_request_value(const DValue& req)
{
DValue r; r["status"]=(f64)0; r["headers"].set_array(); r["body"]=""; r["error"]="";
String method = req.key("method") ? to_upper(req.key("method")->to_string()) : String("GET");
String url = req.key("url") ? req.key("url")->to_string() : String("");
const DValue* method_value = req.key("method");
const DValue* url_value = req.key("url");
const DValue* timeout_value = req.key("timeout_ms");
const DValue* follow_redirects_value = req.key("follow_redirects");
const DValue* headers_value = req.key("headers");
const DValue* body_value = req.key("body");
String method = method_value ? to_upper(method_value->to_string()) : String("GET");
String url = url_value ? url_value->to_string() : String("");
if(url=="" || url.find('\0')!=String::npos) { r["error"]="missing url"; return(r); }
if(method=="") method="GET";
std::vector<String> argv = {"curl", "-sS", "--http1.0", "-X", method, "-D", "-", "-w", "\nUCE_HTTP_STATUS:%{http_code}", "--max-time", std::to_string(std::max<u64>(1, (req.key("timeout_ms") ? req.key("timeout_ms")->to_u64(5000) : 5000) / 1000))};
if(req.key("follow_redirects") && req.key("follow_redirects")->to_bool()) argv.push_back("-L");
if(req.key("headers")) req.key("headers")->each([&](const DValue& v, String k){ if(uce_header_name_safe(k)) { argv.push_back("-H"); argv.push_back(k + ": " + replace(replace(v.to_string(), "\r", " "), "\n", " ")); } });
String body = req.key("body") ? req.key("body")->to_string() : String("");
if(req.key("body")) { argv.push_back("--data-binary"); argv.push_back("@-"); }
u64 timeout_ms = timeout_value ? timeout_value->to_u64(5000) : 5000;
std::vector<String> argv = {"curl", "-sS", "--http1.0", "-X", method, "-D", "-", "-w", "\nUCE_HTTP_STATUS:%{http_code}", "--max-time", std::to_string(std::max<u64>(1, timeout_ms / 1000))};
if(follow_redirects_value && follow_redirects_value->to_bool()) argv.push_back("-L");
if(headers_value) headers_value->each([&](const DValue& v, String k){ if(uce_header_name_safe(k)) { argv.push_back("-H"); argv.push_back(k + ": " + replace(replace(v.to_string(), "\r", " "), "\n", " ")); } });
String body = body_value ? body_value->to_string() : String("");
if(body_value) { argv.push_back("--data-binary"); argv.push_back("@-"); }
argv.push_back(url);
if(access("/usr/bin/curl", X_OK)!=0 && access("/bin/curl", X_OK)!=0) { r["error"]="curl binary not found in runtime PATH"; return(r); }
DValue pr = uce_exec_argv_capture(argv, body, req.key("timeout_ms") ? req.key("timeout_ms")->to_u64(5000) : 5000);
DValue pr = uce_exec_argv_capture(argv, body, timeout_ms);
String out = pr["stdout"].to_string();
String marker="\nUCE_HTTP_STATUS:"; size_t mp=out.rfind(marker);
if(mp!=String::npos) { r["status"]=(f64)strtoull(out.c_str()+mp+marker.size(),0,10); out=out.substr(0,mp); }
@ -989,11 +996,6 @@ public:
return("");
}
String render_entry(const String& entry_source_path)
{
return(invoke_entry(entry_source_path, "render"));
}
String collect(WasmResponse& response)
{
String error = call_core("uce_wasm_finish_output", {}, 0);