From 303e48d37eb2755680b00ba90778fb7b3970d1fc Mon Sep 17 00:00:00 2001 From: hanez Date: Sun, 10 May 2026 02:07:06 +0200 Subject: [PATCH] Added more documentation to kcgi opcodes. No code changes. (0.41.10) --- Doxyfile | 4 +- src/extensions/kcgi.c | 124 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 125 insertions(+), 3 deletions(-) diff --git a/Doxyfile b/Doxyfile index 5775bab..429658f 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = "Fun API Documentation" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 0.41.8 +PROJECT_NUMBER = 0.41.10 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewers a @@ -1131,7 +1131,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which Doxygen is # run. -EXCLUDE = +EXCLUDE = src/vm # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/src/extensions/kcgi.c b/src/extensions/kcgi.c index 2ce4607..c5476b1 100644 --- a/src/extensions/kcgi.c +++ b/src/extensions/kcgi.c @@ -9,7 +9,46 @@ /** * @file kcgi.c - * @brief kcgi helpers for Fun VM KCGI-related opcodes (conditional build). + * @brief Thin kcgi integration helpers used by VM opcodes under src/vm/kcgi/. + * + * This module centralizes compact helpers around the kcgi API so the VM + * opcode snippets included by vm.c can remain minimal and focus on stack + * marshalling. The helpers here do not depend on VM internals beyond the + * Value conversion utilities used to represent HTTP requests as Fun values. + * Keeping the external-API specifics in src/extensions/ mirrors other + * integrations (OpenSSL, PCRE2, SQLite, XML2, JSON, INI) and improves + * maintainability. + * + * Build-time feature flag: + * - All code in this file is compiled only when FUN_WITH_KCGI is enabled. + * When disabled, the file provides no symbols and VM opcodes compiled + * from src/vm/kcgi/*.c will fall back to no-op/falsey behaviors as + * documented in those opcode snippets. + * + * Ownership and memory model: + * - kcgi_parse_request() allocates a struct kreq via khttp_parse() and, on + * success, returns it through the out-parameter; the caller takes ownership + * and must later release it with kcgi_free_request(). + * - kcgi_free_request() calls khttp_free() and then frees the allocation + * wrapper used by this module. + * - kcgi_write_str() does not take ownership of the input string; it may be + * NULL, which is treated as an empty string. + * - kreq_to_fun() and kcgi_fields_to_map() create Fun Values following the + * VM's normal ownership semantics (returned by value to the caller). + * + * Global state and lifetime: + * - A thread-local pointer g_kcgi_req holds the current request for the + * active CGI handling context. VM opcodes are responsible for setting and + * clearing this pointer by calling the helpers exposed here. + * + * Error handling: + * - Functions return 0/NULL on failure and non-zero/valid objects on success. + * Callers should check return values before use. No errno is set. + * + * Thread-safety: + * - The request handle is stored in a thread-local variable to isolate state + * between concurrent executions. Helpers otherwise maintain no global + * mutable state. */ #ifdef FUN_WITH_KCGI @@ -25,6 +64,24 @@ static __declspec(thread) struct kreq *g_kcgi_req = NULL; static __thread struct kreq *g_kcgi_req = NULL; #endif +/** + * Convert kcgi form/query fields to a Fun map Value. + * + * Each entry from r->fields is copied into a newly created Fun map where + * the kcgi key becomes the map key and the kcgi value becomes a Fun string. + * + * Notes: + * - If the request pointer is NULL, an empty map is returned. + * - When multiple kcgi fields share the same key, later occurrences will + * overwrite earlier ones ("last write wins"). + * - Empty or NULL keys/values are converted to empty strings (""). + * + * Ownership: + * - The returned Value is owned by the caller (normal VM semantics). + * + * @param r Parsed kcgi request pointer (may be NULL). + * @return A Fun map Value containing string keys and values. + */ static Value kcgi_fields_to_map(const struct kreq *r) { Value m = make_map_empty(); if (!r) return m; @@ -36,6 +93,22 @@ static Value kcgi_fields_to_map(const struct kreq *r) { return m; } +/** + * Convert a kcgi request handle into a structured Fun Value. + * + * The resulting map contains at least the following keys: + * - "host": string; empty if unavailable + * - "port": int; TCP port + * - "path": string; request path + * - "suffix": string; request suffix (kcgi notion) + * - "fields": map; key/value pairs from form/query fields + * + * Missing fields from kcgi are converted to empty strings where applicable. + * If r is NULL, an empty map is returned. + * + * @param r Parsed kcgi request pointer (may be NULL). + * @return A Fun map Value describing the request. + */ static Value kreq_to_fun(const struct kreq *r) { Value out = make_map_empty(); if (!r) return out; @@ -51,6 +124,24 @@ static Value kreq_to_fun(const struct kreq *r) { } /* Lifecycle helpers used by VM opcodes */ +/** + * Parse the current CGI/FCGI request using kcgi. + * + * On success, this allocates and initializes a struct kreq by calling + * khttp_parse() and stores it in *out. The caller takes ownership of the + * returned handle and must later free it with kcgi_free_request(). + * + * Behavior details: + * - All keys are accepted (kvalid array with a single {NULL,NULL} entry). + * - On allocation or parse failure, *out is not modified and 0 is returned. + * + * Threading: + * - This function is independent of the thread-local g_kcgi_req; setting that + * pointer is left to the caller/opcode. + * + * @param[out] out Where to store the newly allocated struct kreq on success. + * @return 1 on success, 0 on failure. + */ static int kcgi_parse_request(struct kreq **out) { static const struct kvalid keys[] = { { NULL, NULL } }; /* accept all */ struct kreq *r = (struct kreq *)calloc(1, sizeof(*r)); @@ -61,12 +152,34 @@ static int kcgi_parse_request(struct kreq **out) { return 1; } +/** + * Free a request previously returned by kcgi_parse_request(). + * + * Safe to call with NULL. + * + * @param r Request handle to free (may be NULL). + */ static void kcgi_free_request(struct kreq *r) { if (!r) return; khttp_free(r); free(r); } +/** + * Begin the HTTP response for the current request. + * + * This emits an optional Content-Type header and then switches into body + * mode by calling khttp_body(). If g_kcgi_req is not set, the call fails. + * + * Notes: + * - The status code parameter is currently not passed to kcgi; unless set + * elsewhere, kcgi will default the status to 200 OK. + * - If ctype is NULL or empty, no Content-Type header is emitted here. + * + * @param code Suggested HTTP status code (reserved for future use). + * @param ctype MIME type to emit as Content-Type (may be NULL/empty). + * @return 1 on success, 0 on failure. + */ static int kcgi_reply_start(int code, const char *ctype) { if (!g_kcgi_req) return 0; /* Emit Content-Type header; Status defaults to 200 if not set */ @@ -79,6 +192,15 @@ static int kcgi_reply_start(int code, const char *ctype) { return 1; } +/** + * Write a UTF-8 string to the HTTP response body. + * + * Requires an active request in g_kcgi_req and an initialized response body + * (kcgi_reply_start() or equivalent must have been called). + * + * @param s NUL-terminated string to write; NULL is treated as "". + * @return 1 on success, 0 on failure. + */ static int kcgi_write_str(const char *s) { if (!g_kcgi_req) return 0; if (!s) s = "";