From 71ddcaf7d48ffd1343e597e4715b570c922a306c Mon Sep 17 00:00:00 2001 From: Udo Date: Thu, 21 May 2026 10:31:21 +0000 Subject: [PATCH] Consolidate config and base64 helpers --- site/tests/core.uce | 11 +++ src/fastcgi/src/fcgicc.cc | 38 +++-------- src/lib/compiler.cpp | 14 +--- src/lib/sys.cpp | 54 +++++++++++++++ src/lib/sys.h | 7 ++ src/lib/uri.cpp | 19 ++++-- src/lib/uri.h | 2 + src/lib/zip.cpp | 14 +--- src/linux_fastcgi.cpp | 138 ++++---------------------------------- 9 files changed, 113 insertions(+), 184 deletions(-) diff --git a/site/tests/core.uce b/site/tests/core.uce index 1e31fc7..e85e142 100644 --- a/site/tests/core.uce +++ b/site/tests/core.uce @@ -43,6 +43,17 @@ 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")); + String binary_payload = "core"; + binary_payload.push_back((char)0x00); + binary_payload.push_back((char)0xff); + binary_payload += "payload"; + bool base64_ok = false; + String encoded_base64 = base64_encode(binary_payload); + String decoded_base64 = base64_decode(encoded_base64, base64_ok); + bool invalid_base64_ok = true; + base64_decode("AA=A", invalid_base64_ok); + check("base64_encode() / base64_decode() binary-safe", base64_ok && decoded_base64 == binary_payload && decoded_base64.size() == binary_payload.size() && !invalid_base64_ok, encoded_base64 + " bytes=" + std::to_string((u64)decoded_base64.size())); + String utf8_sample = "A\xC3\xA9"; auto utf8_parts = split_utf8(utf8_sample); check("split_utf8()", utf8_parts.size() == 2, "count=" + std::to_string(utf8_parts.size())); diff --git a/src/fastcgi/src/fcgicc.cc b/src/fastcgi/src/fcgicc.cc index 7cee1f4..3aa622d 100644 --- a/src/fastcgi/src/fcgicc.cc +++ b/src/fastcgi/src/fcgicc.cc @@ -73,38 +73,18 @@ struct TransportLimits { } }; -u64 transport_config_u64(String key, u64 fallback) -{ - if(!context || !context->server) - return(fallback); - String raw = trim(context->server->config[key]); - if(raw == "") - return(fallback); - return(int_val(raw)); -} - -f64 transport_config_f64(String key, f64 fallback) -{ - if(!context || !context->server) - return(fallback); - String raw = trim(context->server->config[key]); - if(raw == "") - return(fallback); - return(float_val(raw)); -} - TransportLimits transport_limits() { TransportLimits limits; - limits.max_client_connections = transport_config_u64("TRANSPORT_MAX_CLIENT_CONNECTIONS", limits.max_client_connections); - limits.max_http_header_bytes = transport_config_u64("TRANSPORT_MAX_HTTP_HEADER_BYTES", limits.max_http_header_bytes); - limits.max_http_body_bytes = transport_config_u64("TRANSPORT_MAX_HTTP_BODY_BYTES", limits.max_http_body_bytes); - limits.max_websocket_frame_bytes = transport_config_u64("TRANSPORT_MAX_WEBSOCKET_FRAME_BYTES", limits.max_websocket_frame_bytes); - limits.max_websocket_message_bytes = transport_config_u64("TRANSPORT_MAX_WEBSOCKET_MESSAGE_BYTES", limits.max_websocket_message_bytes); - limits.max_websocket_output_bytes = transport_config_u64("TRANSPORT_MAX_WEBSOCKET_OUTPUT_BYTES", limits.max_websocket_output_bytes); - limits.max_response_bytes = transport_config_u64("TRANSPORT_MAX_RESPONSE_BYTES", limits.max_response_bytes); - limits.http_request_timeout_seconds = transport_config_f64("TRANSPORT_HTTP_REQUEST_TIMEOUT_SECONDS", limits.http_request_timeout_seconds); - limits.connection_idle_timeout_seconds = transport_config_f64("TRANSPORT_CONNECTION_IDLE_TIMEOUT_SECONDS", limits.connection_idle_timeout_seconds); + limits.max_client_connections = config_u64("TRANSPORT_MAX_CLIENT_CONNECTIONS", limits.max_client_connections); + limits.max_http_header_bytes = config_u64("TRANSPORT_MAX_HTTP_HEADER_BYTES", limits.max_http_header_bytes); + limits.max_http_body_bytes = config_u64("TRANSPORT_MAX_HTTP_BODY_BYTES", limits.max_http_body_bytes); + limits.max_websocket_frame_bytes = config_u64("TRANSPORT_MAX_WEBSOCKET_FRAME_BYTES", limits.max_websocket_frame_bytes); + limits.max_websocket_message_bytes = config_u64("TRANSPORT_MAX_WEBSOCKET_MESSAGE_BYTES", limits.max_websocket_message_bytes); + limits.max_websocket_output_bytes = config_u64("TRANSPORT_MAX_WEBSOCKET_OUTPUT_BYTES", limits.max_websocket_output_bytes); + limits.max_response_bytes = config_u64("TRANSPORT_MAX_RESPONSE_BYTES", limits.max_response_bytes); + limits.http_request_timeout_seconds = config_f64("TRANSPORT_HTTP_REQUEST_TIMEOUT_SECONDS", limits.http_request_timeout_seconds); + limits.connection_idle_timeout_seconds = config_f64("TRANSPORT_CONNECTION_IDLE_TIMEOUT_SECONDS", limits.connection_idle_timeout_seconds); return(limits); } diff --git a/src/lib/compiler.cpp b/src/lib/compiler.cpp index fb714f6..e020426 100644 --- a/src/lib/compiler.cpp +++ b/src/lib/compiler.cpp @@ -56,23 +56,11 @@ void compiler_unload_failed_shared_unit(SharedUnit* su); bool compiler_run_unit_init(Request* context, SharedUnit* su, String* error_out = 0); bool compiler_run_unit_once_if_needed(Request* context, SharedUnit* su, String* error_out = 0); -bool compiler_config_truthy(String raw, bool default_value) -{ - raw = to_lower(trim(raw)); - if(raw == "") - return(default_value); - if(raw == "1" || raw == "true" || raw == "yes" || raw == "on") - return(true); - if(raw == "0" || raw == "false" || raw == "no" || raw == "off") - return(false); - return(default_value); -} - bool compiler_jit_compile_on_request_enabled(Request* context) { if(!context || !context->server) return(true); - return(compiler_config_truthy(context->server->config["JIT_COMPILE_ON_REQUEST"], true)); + return(config_bool("JIT_COMPILE_ON_REQUEST", true)); } bool compiler_is_u64_string(String value) diff --git a/src/lib/sys.cpp b/src/lib/sys.cpp index e8256e6..4531df3 100644 --- a/src/lib/sys.cpp +++ b/src/lib/sys.cpp @@ -868,6 +868,60 @@ StringList ls(String dir) return(split(trim(shell_exec("ls -1 "+shell_escape(dir))), "\n")); } +u64 config_map_u64(StringMap& cfg, String key, u64 fallback) +{ + String raw = trim(cfg[key]); + if(raw == "") + return(fallback); + return(int_val(raw)); +} + +f64 config_map_f64(StringMap& cfg, String key, f64 fallback) +{ + String raw = trim(cfg[key]); + if(raw == "") + return(fallback); + return(float_val(raw)); +} + +bool config_bool_value(String raw, bool fallback) +{ + raw = trim(to_lower(raw)); + if(raw == "") + return(fallback); + if(raw == "1" || raw == "true" || raw == "yes" || raw == "on") + return(true); + if(raw == "0" || raw == "false" || raw == "no" || raw == "off") + return(false); + return(fallback); +} + +bool config_map_bool(StringMap& cfg, String key, bool fallback) +{ + return(config_bool_value(cfg[key], fallback)); +} + +u64 config_u64(String key, u64 fallback) +{ + if(!context || !context->server) + return(fallback); + return(config_map_u64(context->server->config, key, fallback)); +} + +f64 config_f64(String key, f64 fallback) +{ + if(!context || !context->server) + return(fallback); + return(config_map_f64(context->server->config, key, fallback)); +} + +bool config_bool(String key, bool fallback) +{ + if(!context || !context->server) + return(fallback); + return(config_map_bool(context->server->config, key, fallback)); +} + StringMap make_server_settings() { StringMap cfg; diff --git a/src/lib/sys.h b/src/lib/sys.h index d28be4b..608c82e 100644 --- a/src/lib/sys.h +++ b/src/lib/sys.h @@ -34,6 +34,13 @@ time_t file_mtime(String file_name); void file_unlink(String file_name); String expand_path(String path, String relative_to_path = ""); StringList ls(String dir); +u64 config_map_u64(StringMap& cfg, String key, u64 fallback); +f64 config_map_f64(StringMap& cfg, String key, f64 fallback); +bool config_bool_value(String raw, bool fallback = true); +bool config_map_bool(StringMap& cfg, String key, bool fallback = true); +u64 config_u64(String key, u64 fallback); +f64 config_f64(String key, f64 fallback); +bool config_bool(String key, bool fallback = true); f64 time_precise(); u64 time(); diff --git a/src/lib/uri.cpp b/src/lib/uri.cpp index d9cba38..cf7fb40 100644 --- a/src/lib/uri.cpp +++ b/src/lib/uri.cpp @@ -4,7 +4,7 @@ #include #include -static String base64_encode(String raw) +String base64_encode(String raw) { static const char* chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -31,7 +31,9 @@ static String base64_encode(String raw) return(result); } -static int base64_decode_value(char c) +namespace { + +int base64_decode_value(char c) { if(c >= 'A' && c <= 'Z') return(c - 'A'); @@ -46,17 +48,24 @@ static int base64_decode_value(char c) return(-1); } -static String base64_decode(String raw, bool& ok) +} + +String base64_decode(String raw, bool& ok) { ok = false; String cleaned; for(char c : raw) { - if(!isspace(c)) + if(!isspace((unsigned char)c)) cleaned.append(1, c); } - if(cleaned.length() == 0 || (cleaned.length() % 4) != 0) + if(cleaned.length() == 0) + { + ok = true; + return(""); + } + if((cleaned.length() % 4) != 0) return(""); String result; diff --git a/src/lib/uri.h b/src/lib/uri.h index c3ae573..b93683a 100644 --- a/src/lib/uri.h +++ b/src/lib/uri.h @@ -3,6 +3,8 @@ String var_dump(URI uri, String prefix = "", String postfix = "\n"); +String base64_encode(String raw); +String base64_decode(String raw, bool& ok); String uri_decode(String q); String uri_encode(String q); StringMap parse_query(String q); diff --git a/src/lib/zip.cpp b/src/lib/zip.cpp index 1ca5350..55d31da 100644 --- a/src/lib/zip.cpp +++ b/src/lib/zip.cpp @@ -14,19 +14,9 @@ String zip_error(String api, String detail) return(api + "(): " + detail); } -u64 archive_config_u64(String key, u64 fallback) -{ - if(!context || !context->server) - return(fallback); - String raw = trim(context->server->config[key]); - if(raw == "") - return(fallback); - return(int_val(raw)); -} - void archive_check_size(String api, String label, u64 size, String config_key, u64 fallback) { - u64 limit = archive_config_u64(config_key, fallback); + u64 limit = config_u64(config_key, fallback); if(limit > 0 && size > limit) throw std::runtime_error(zip_error(api, label + " exceeds configured limit")); } @@ -336,7 +326,7 @@ String gz_uncompress(String compressed) if(!out) throw std::runtime_error("gz_uncompress(): decompression failed"); - u64 output_limit = archive_config_u64("ARCHIVE_MAX_OUTPUT_BYTES", 64 * 1024 * 1024); + u64 output_limit = config_u64("ARCHIVE_MAX_OUTPUT_BYTES", 64 * 1024 * 1024); if(output_limit > 0 && out_len > output_limit) { mz_free(out); diff --git a/src/linux_fastcgi.cpp b/src/linux_fastcgi.cpp index a74330e..dcb1d14 100644 --- a/src/linux_fastcgi.cpp +++ b/src/linux_fastcgi.cpp @@ -136,90 +136,6 @@ void restore_request_fault_handlers() namespace { -const char* websocket_ipc_base64_alphabet = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -String websocket_ipc_base64_encode(String raw) -{ - String result; - size_t i = 0; - while(i < raw.length()) - { - unsigned char input[3] = {0, 0, 0}; - size_t chunk = 0; - for(; chunk < 3 && i < raw.length(); ++chunk, ++i) - input[chunk] = (unsigned char)raw[i]; - - result += websocket_ipc_base64_alphabet[input[0] >> 2]; - result += websocket_ipc_base64_alphabet[((input[0] & 0x03) << 4) | (input[1] >> 4)]; - result += (chunk > 1 ? websocket_ipc_base64_alphabet[((input[1] & 0x0F) << 2) | (input[2] >> 6)] : '='); - result += (chunk > 2 ? websocket_ipc_base64_alphabet[input[2] & 0x3F] : '='); - } - return(result); -} - -int websocket_ipc_base64_value(char c) -{ - if(c >= 'A' && c <= 'Z') - return(c - 'A'); - if(c >= 'a' && c <= 'z') - return(c - 'a' + 26); - if(c >= '0' && c <= '9') - return(c - '0' + 52); - if(c == '+') - return(62); - if(c == '/') - return(63); - return(-1); -} - -String websocket_ipc_base64_decode(String raw, bool& ok) -{ - ok = false; - String filtered = ""; - for(auto c : raw) - { - if(!isspace((unsigned char)c)) - filtered.append(1, c); - } - if(filtered == "") - { - ok = true; - return(""); - } - if(filtered.length() % 4 != 0) - return(""); - - String result; - for(size_t i = 0; i < filtered.length(); i += 4) - { - int values[4] = {0, 0, 0, 0}; - int padding = 0; - for(int j = 0; j < 4; ++j) - { - char c = filtered[i + j]; - if(c == '=') - { - values[j] = 0; - padding += 1; - continue; - } - values[j] = websocket_ipc_base64_value(c); - if(values[j] < 0) - return(""); - } - - result.append(1, (char)((values[0] << 2) | (values[1] >> 4))); - if(padding < 2) - result.append(1, (char)(((values[1] & 0x0F) << 4) | (values[2] >> 2))); - if(padding < 1) - result.append(1, (char)(((values[2] & 0x03) << 6) | values[3])); - } - - ok = true; - return(result); -} - bool websocket_ipc_set_nonblocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); @@ -323,7 +239,7 @@ DTree websocket_exec_make_message_command(String action, String message, bool bi DTree command; command["action"] = action; command["binary"].set_bool(binary); - command["message_b64"] = websocket_ipc_base64_encode(message); + command["message_b64"] = base64_encode(message); return(command); } @@ -343,7 +259,7 @@ void websocket_exec_apply_command(DTree command) if(action == "broadcast") { bool ok = false; - String payload = websocket_ipc_base64_decode(command["message_b64"].to_string(), ok); + String payload = base64_decode(command["message_b64"].to_string(), ok); if(!ok) return; server.websocket_broadcast(command["scope"].to_string(), payload, command["binary"].to_bool()); @@ -352,7 +268,7 @@ void websocket_exec_apply_command(DTree command) if(action == "send_to") { bool ok = false; - String payload = websocket_ipc_base64_decode(command["message_b64"].to_string(), ok); + String payload = base64_decode(command["message_b64"].to_string(), ok); if(!ok) return; server.websocket_send_to(command["connection_id"].to_string(), payload, command["binary"].to_bool()); @@ -543,7 +459,7 @@ void websocket_exec_process_job_line(int fd, String line) return; bool decoded = false; - String message = websocket_ipc_base64_decode(job["message_b64"].to_string(), decoded); + String message = base64_decode(job["message_b64"].to_string(), decoded); if(!decoded) { printf("(!) invalid websocket IPC payload for %s\n", job["connection_id"].to_string().c_str()); @@ -705,18 +621,6 @@ String normalize_ws_scope(String scope) return(expand_path(scope, cwd_get())); } -bool config_truthy(String raw, bool default_value = true) -{ - raw = to_lower(trim(raw)); - if(raw == "") - return(default_value); - if(raw == "1" || raw == "true" || raw == "yes" || raw == "on") - return(true); - if(raw == "0" || raw == "false" || raw == "no" || raw == "off") - return(false); - return(default_value); -} - String ws_connection_id() { if(!context) @@ -1063,7 +967,7 @@ int handle_websocket_message(FastCGIRequest& request, const String& message, u8 job["opcode"] = (f64)opcode; job["is_binary"].set_bool(request.resources.websocket_is_binary); job["is_text"].set_bool(request.resources.websocket_is_text); - job["message_b64"] = websocket_ipc_base64_encode(message); + job["message_b64"] = base64_encode(message); if(request.resources.websocket_connection_state) job["connection_state"] = *request.resources.websocket_connection_state; @@ -1169,22 +1073,6 @@ StringMap custom_server_config_decode(String content) return(result); } -u64 custom_server_config_u64(String key, u64 fallback) -{ - String raw = trim(server_state.config[key]); - if(raw == "") - return(fallback); - return(int_val(raw)); -} - -bool custom_server_config_truthy(String key, bool fallback) -{ - String raw = to_lower(trim(server_state.config[key])); - if(raw == "") - return(fallback); - return(raw == "1" || raw == "true" || raw == "yes" || raw == "on"); -} - bool custom_server_is_numeric_port(String value) { value = trim(value); @@ -1227,11 +1115,11 @@ int custom_server_bind_http(FastCGIServer& dispatcher, String bind) if(custom_server_is_numeric_port(bind)) { u64 port = int_val(bind); - u64 min_port = custom_server_config_u64("CUSTOM_SERVER_MIN_PORT", 1024); - u64 max_port = custom_server_config_u64("CUSTOM_SERVER_MAX_PORT", 65535); + u64 min_port = config_map_u64(server_state.config, "CUSTOM_SERVER_MIN_PORT", 1024); + u64 max_port = config_map_u64(server_state.config, "CUSTOM_SERVER_MAX_PORT", 65535); if(port < min_port || port > max_port) throw std::runtime_error("server_start_http(): TCP port is outside configured custom server range"); - String bind_address = custom_server_config_truthy("CUSTOM_SERVER_ALLOW_PUBLIC_BIND", false) ? "0.0.0.0" : "127.0.0.1"; + String bind_address = config_map_bool(server_state.config, "CUSTOM_SERVER_ALLOW_PUBLIC_BIND", false) ? "0.0.0.0" : "127.0.0.1"; return(dispatcher.listen_http((unsigned)port, bind_address)); } String socket_prefix = first(server_state.config["CUSTOM_SERVER_UNIX_SOCKET_PREFIX"], "/tmp/uce/custom-servers/"); @@ -1270,7 +1158,7 @@ int custom_server_http_complete(FastCGIRequest& request) request.params["UCE_SERVE_HTTP_BIND"] = cfg["bind"]; request.params["UCE_SERVE_HTTP_FUNCTION"] = cfg["function"]; request.params["SCRIPT_FILENAME"] = cfg["file"]; - u64 timeout = custom_server_config_u64("CUSTOM_SERVER_HANDLER_TIMEOUT_SECONDS", 30); + u64 timeout = config_map_u64(server_state.config, "CUSTOM_SERVER_HANDLER_TIMEOUT_SECONDS", 30); if(timeout > 0) alarm(timeout); int status = handle_complete(request); @@ -1341,7 +1229,7 @@ pid_t server_start_http(String key, String socket_fn_or_port, String call_uce_fi previous_config = custom_server_config_decode(file_get_contents(config_file)); String new_config = custom_server_config_encode(key, "http", socket_fn_or_port, call_uce_filename, call_function); String task_key = custom_server_task_key(key); - u64 max_servers = custom_server_config_u64("CUSTOM_SERVER_MAX_SERVERS", 16); + u64 max_servers = config_map_u64(server_state.config, "CUSTOM_SERVER_MAX_SERVERS", 16); if(!file_exists(config_file) && max_servers > 0 && custom_server_registry_count() >= max_servers) throw std::runtime_error("server_start_http(): custom server quota exceeded"); pid_t existing_pid = task_pid(task_key); @@ -1392,7 +1280,8 @@ void run_proactive_compiler() Request background_context; StringList compile_queue; background_context.server = &server_state; - if(!config_truthy(server_state.config["PROACTIVE_COMPILE_ENABLED"], true)) + set_active_request(background_context); + if(!config_map_bool(server_state.config, "PROACTIVE_COMPILE_ENABLED", true)) return; f64 check_interval = float_val(server_state.config["PROACTIVE_COMPILE_CHECK_INTERVAL"]); f64 failure_retry_interval = 0; @@ -1406,7 +1295,6 @@ void run_proactive_compiler() ); my_pid = getpid(); - context = &background_context; close_inherited_server_sockets(); signal(SIGSEGV, on_segfault); @@ -1497,7 +1385,7 @@ bool proactive_compiler_alive() void ensure_proactive_compiler() { - if(!config_truthy(server_state.config["PROACTIVE_COMPILE_ENABLED"], true)) + if(!config_map_bool(server_state.config, "PROACTIVE_COMPILE_ENABLED", true)) return; if(float_val(server_state.config["PROACTIVE_COMPILE_CHECK_INTERVAL"]) <= 0) return;