Fun API Documentation 0.42.1
The programming language that makes you have fun!
Loading...
Searching...
No Matches
kcgi.c
Go to the documentation of this file.
1/*
2 * This file is part of the Fun programming language.
3 * https://fun-lang.xyz/
4 *
5 * Copyright 2026 Johannes Findeisen
6 * Licensed under the terms of the Apache-2.0 license.
7 * https://opensource.org/license/apache-2-0
8 */
9
10/**
11 * @file kcgi.c
12 * @brief Thin kcgi integration helpers used by VM opcodes under src/vm/kcgi/.
13 *
14 * This module centralizes compact helpers around the kcgi API so the VM
15 * opcode snippets included by vm.c can remain minimal and focus on stack
16 * marshalling. The helpers here do not depend on VM internals beyond the
17 * Value conversion utilities used to represent HTTP requests as Fun values.
18 * Keeping the external-API specifics in src/extensions/ mirrors other
19 * integrations (OpenSSL, PCRE2, SQLite, XML2, JSON, INI) and improves
20 * maintainability.
21 *
22 * Build-time feature flag:
23 * - All code in this file is compiled only when FUN_WITH_KCGI is enabled.
24 * When disabled, the file provides no symbols and VM opcodes compiled
25 * from src/vm/kcgi/*.c will fall back to no-op/falsey behaviors as
26 * documented in those opcode snippets.
27 *
28 * Ownership and memory model:
29 * - kcgi_parse_request() allocates a struct kreq via khttp_parse() and, on
30 * success, returns it through the out-parameter; the caller takes ownership
31 * and must later release it with kcgi_free_request().
32 * - kcgi_free_request() calls khttp_free() and then frees the allocation
33 * wrapper used by this module.
34 * - kcgi_write_str() does not take ownership of the input string; it may be
35 * NULL, which is treated as an empty string.
36 * - kreq_to_fun() and kcgi_fields_to_map() create Fun Values following the
37 * VM's normal ownership semantics (returned by value to the caller).
38 *
39 * Global state and lifetime:
40 * - A thread-local pointer g_kcgi_req holds the current request for the
41 * active CGI handling context. VM opcodes are responsible for setting and
42 * clearing this pointer by calling the helpers exposed here.
43 *
44 * Error handling:
45 * - Functions return 0/NULL on failure and non-zero/valid objects on success.
46 * Callers should check return values before use. No errno is set.
47 *
48 * Thread-safety:
49 * - The request handle is stored in a thread-local variable to isolate state
50 * between concurrent executions. Helpers otherwise maintain no global
51 * mutable state.
52 */
53
54#ifdef FUN_WITH_KCGI
55
56#include <kcgi.h>
57#include <stdlib.h>
58#include <string.h>
59
60/* Thread-local request handle for the current CGI invocation */
61#ifdef _WIN32
62static __declspec(thread) struct kreq *g_kcgi_req = NULL;
63#else
64static __thread struct kreq *g_kcgi_req = NULL;
65#endif
66
67/**
68 * Convert kcgi form/query fields to a Fun map Value.
69 *
70 * Each entry from r->fields is copied into a newly created Fun map where
71 * the kcgi key becomes the map key and the kcgi value becomes a Fun string.
72 *
73 * Notes:
74 * - If the request pointer is NULL, an empty map is returned.
75 * - When multiple kcgi fields share the same key, later occurrences will
76 * overwrite earlier ones ("last write wins").
77 * - Empty or NULL keys/values are converted to empty strings ("").
78 *
79 * Ownership:
80 * - The returned Value is owned by the caller (normal VM semantics).
81 *
82 * @param r Parsed kcgi request pointer (may be NULL).
83 * @return A Fun map Value containing string keys and values.
84 */
85static Value kcgi_fields_to_map(const struct kreq *r) {
87 if (!r) return m;
88 for (size_t i = 0; i < r->fieldsz; i++) {
89 const char *k = r->fields[i].key ? r->fields[i].key : "";
90 const char *v = r->fields[i].val ? r->fields[i].val : "";
91 map_set(&m, k, make_string(v));
92 }
93 return m;
94}
95
96/**
97 * Convert a kcgi request handle into a structured Fun Value.
98 *
99 * The resulting map contains at least the following keys:
100 * - "host": string; empty if unavailable
101 * - "port": int; TCP port
102 * - "path": string; request path
103 * - "suffix": string; request suffix (kcgi notion)
104 * - "fields": map; key/value pairs from form/query fields
105 *
106 * Missing fields from kcgi are converted to empty strings where applicable.
107 * If r is NULL, an empty map is returned.
108 *
109 * @param r Parsed kcgi request pointer (may be NULL).
110 * @return A Fun map Value describing the request.
111 */
112static Value kreq_to_fun(const struct kreq *r) {
113 Value out = make_map_empty();
114 if (!r) return out;
115 /* Basic request info */
116 map_set(&out, "host", make_string(r->host ? r->host : ""));
117 map_set(&out, "port", make_int((int64_t)r->port));
118 map_set(&out, "path", make_string(r->path ? r->path : ""));
119 map_set(&out, "suffix", make_string(r->suffix ? r->suffix : ""));
120
121 Value fields = kcgi_fields_to_map(r);
122 map_set(&out, "fields", fields);
123 return out;
124}
125
126/* Lifecycle helpers used by VM opcodes */
127/**
128 * Parse the current CGI/FCGI request using kcgi.
129 *
130 * On success, this allocates and initializes a struct kreq by calling
131 * khttp_parse() and stores it in *out. The caller takes ownership of the
132 * returned handle and must later free it with kcgi_free_request().
133 *
134 * Behavior details:
135 * - All keys are accepted (kvalid array with a single {NULL,NULL} entry).
136 * - On allocation or parse failure, *out is not modified and 0 is returned.
137 *
138 * Threading:
139 * - This function is independent of the thread-local g_kcgi_req; setting that
140 * pointer is left to the caller/opcode.
141 *
142 * @param[out] out Where to store the newly allocated struct kreq on success.
143 * @return 1 on success, 0 on failure.
144 */
145static int kcgi_parse_request(struct kreq **out) {
146 static const struct kvalid keys[] = { { NULL, NULL } }; /* accept all */
147 struct kreq *r = (struct kreq *)calloc(1, sizeof(*r));
148 if (!r) return 0;
149 enum kcgi_err er = khttp_parse(r, keys, 0, NULL, 0, 0);
150 if (er != KCGI_OK) { free(r); return 0; }
151 *out = r;
152 return 1;
153}
154
155/**
156 * Free a request previously returned by kcgi_parse_request().
157 *
158 * Safe to call with NULL.
159 *
160 * @param r Request handle to free (may be NULL).
161 */
162static void kcgi_free_request(struct kreq *r) {
163 if (!r) return;
164 khttp_free(r);
165 free(r);
166}
167
168/**
169 * Begin the HTTP response for the current request.
170 *
171 * This emits an optional Content-Type header and then switches into body
172 * mode by calling khttp_body(). If g_kcgi_req is not set, the call fails.
173 *
174 * Notes:
175 * - The status code parameter is currently not passed to kcgi; unless set
176 * elsewhere, kcgi will default the status to 200 OK.
177 * - If ctype is NULL or empty, no Content-Type header is emitted here.
178 *
179 * @param code Suggested HTTP status code (reserved for future use).
180 * @param ctype MIME type to emit as Content-Type (may be NULL/empty).
181 * @return 1 on success, 0 on failure.
182 */
183static int kcgi_reply_start(int code, const char *ctype) {
184 if (!g_kcgi_req) return 0;
185 /* Emit Content-Type header; Status defaults to 200 if not set */
186 if (ctype && *ctype) {
187 if (khttp_head(g_kcgi_req, "Content-Type", "%s", ctype) != KCGI_OK)
188 return 0;
189 }
190 if (khttp_body(g_kcgi_req) != KCGI_OK)
191 return 0;
192 return 1;
193}
194
195/**
196 * Write a UTF-8 string to the HTTP response body.
197 *
198 * Requires an active request in g_kcgi_req and an initialized response body
199 * (kcgi_reply_start() or equivalent must have been called).
200 *
201 * @param s NUL-terminated string to write; NULL is treated as "".
202 * @return 1 on success, 0 on failure.
203 */
204static int kcgi_write_str(const char *s) {
205 if (!g_kcgi_req) return 0;
206 if (!s) s = "";
207 size_t n = strlen(s);
208 return khttp_write(g_kcgi_req, s, n) == KCGI_OK;
209}
210
211#endif /* FUN_WITH_KCGI */
int map_set(Value *vm, const char *key, Value v)
Insert or replace a key in the map.
Definition map.c:79
Value make_map_empty(void)
Construct a new empty map Value.
Definition map.c:35
Tagged union representing a Fun value.
Definition value.h:68
Value make_string(const char *s)
Construct a string Value by duplicating the given C string.
Definition value.c:95
Value make_int(int64_t v)
Construct a Value representing a 64-bit integer.
Definition value.c:51