Added some basic CGI support using kcgi plus a lot of fixes and content updates. (0.41.9)
This commit is contained in:
parent
9698074279
commit
604c415c0c
25 changed files with 407 additions and 13 deletions
|
|
@ -1,5 +1,5 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
project(fun VERSION 0.41.8 LANGUAGES C)
|
||||
project(fun VERSION 0.41.9 LANGUAGES C)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
|
|
@ -430,6 +430,26 @@ if(BUILD_TESTING)
|
|||
else()
|
||||
message(WARNING "include_line_mapping_test.fun not found; skipping include_line_mapping CTest")
|
||||
endif()
|
||||
|
||||
# KCGI example smoke test (only when the KCGI extension is enabled)
|
||||
# We run the CGI example with minimal environment to avoid RFC warnings
|
||||
# and assert the body contains the expected greeting.
|
||||
if(FUN_WITH_KCGI)
|
||||
set(_kcgi_example "${CMAKE_SOURCE_DIR}/examples/cgi/hello_kcgi.fun")
|
||||
if(EXISTS "${_kcgi_example}")
|
||||
add_test(NAME kcgi_hello
|
||||
COMMAND $<TARGET_FILE:fun> "${_kcgi_example}"
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
# Provide standard library path and minimal CGI environment
|
||||
set_tests_properties(kcgi_hello PROPERTIES
|
||||
ENVIRONMENT "FUN_LIB_DIR=${CMAKE_SOURCE_DIR}/lib;REQUEST_METHOD=GET;QUERY_STRING=name=Fun;SERVER_NAME=localhost;SERVER_PORT=80;SCRIPT_NAME=/hello_kcgi.fun;REMOTE_ADDR=127.0.0.1"
|
||||
PASS_REGULAR_EXPRESSION "Hello, Fun!"
|
||||
)
|
||||
else()
|
||||
message(WARNING "KCGI example not found: ${_kcgi_example}; skipping kcgi_hello CTest")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# --- Doxygen docs target (optional) ---
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ See [./lib/](https://git.xw3.org/fun/fun/src/branch/main/lib) for what the stand
|
|||
|
||||
### Optional extensions (build-time selectable / only testing this on Linux actually):
|
||||
|
||||
- [CGI](https://en.wikipedia.org/wiki/Common_Gateway_Interface) support builtin using [kcgi](https://kristaps.bsd.lv/kcgi/) (optional) ☐
|
||||
- [CGI](https://en.wikipedia.org/wiki/Common_Gateway_Interface) support builtin using [kcgi](https://kristaps.bsd.lv/kcgi/) (optional) — see [docs](./web/documentation/extensions/kcgi/kcgi.md) ☑
|
||||
- [cURL (libcurl)](./web/documentation/extensions/curl/curl.md) (optional) ☑
|
||||
- [INI (iniparser)](./web/documentation/extensions/ini/ini.md) (optional) ☑
|
||||
- [JSON (json-c)](./web/documentation/extensions/json/json.md) (optional) ☑
|
||||
|
|
|
|||
|
|
@ -20,17 +20,18 @@ include(${CMAKE_SOURCE_DIR}/cmake/Extensions/PCRE2.cmake)
|
|||
include(${CMAKE_SOURCE_DIR}/cmake/Extensions/CURL.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/Extensions/REPL.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/Extensions/OPENSSL.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/Extensions/KCGI.cmake)
|
||||
|
||||
# Summary of extension toggles
|
||||
message(STATUS "---- Fun extension summary ----")
|
||||
_fun_print_feature("SQLite (FUN_WITH_SQLITE)" FUN_WITH_SQLITE)
|
||||
_fun_print_feature("PCSC (FUN_WITH_PCSC)" FUN_WITH_PCSC)
|
||||
_fun_print_feature("JSON-C (FUN_WITH_JSON)" FUN_WITH_JSON)
|
||||
_fun_print_feature("json-c (FUN_WITH_JSON)" FUN_WITH_JSON)
|
||||
_fun_print_feature("INI/iniparser (FUN_WITH_INI)" FUN_WITH_INI)
|
||||
_fun_print_feature("libxml2 (FUN_WITH_XML2)" FUN_WITH_XML2)
|
||||
## libsql extension removed
|
||||
_fun_print_feature("PCRE2 (FUN_WITH_PCRE2)" FUN_WITH_PCRE2)
|
||||
_fun_print_feature("libcurl (FUN_WITH_CURL)" FUN_WITH_CURL)
|
||||
_fun_print_feature("curl (FUN_WITH_CURL)" FUN_WITH_CURL)
|
||||
_fun_print_feature("REPL (FUN_WITH_REPL)" FUN_WITH_REPL)
|
||||
_fun_print_feature("OpenSSL (FUN_WITH_OPENSSL)" FUN_WITH_OPENSSL)
|
||||
_fun_print_feature("kcgi (FUN_WITH_KCGI)" FUN_WITH_KCGI)
|
||||
message(STATUS "--------------------------------")
|
||||
|
|
|
|||
19
cmake/Extensions/KCGI.cmake
Normal file
19
cmake/Extensions/KCGI.cmake
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# KCGI (kcgi)
|
||||
option(FUN_WITH_KCGI "Enable kcgi support (CGI/FastCGI via kcgi)" OFF)
|
||||
set(KCGI_INCLUDE_DIRS "")
|
||||
set(KCGI_LINK_LIBS "")
|
||||
|
||||
if(FUN_WITH_KCGI)
|
||||
add_definitions(-DFUN_WITH_KCGI)
|
||||
find_package(PkgConfig QUIET)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(KCGI QUIET kcgi)
|
||||
endif()
|
||||
if(KCGI_FOUND)
|
||||
list(APPEND KCGI_INCLUDE_DIRS ${KCGI_INCLUDE_DIRS} ${KCGI_INCLUDE_DIRS})
|
||||
list(APPEND KCGI_LINK_LIBS ${KCGI_LINK_LIBS} ${KCGI_LIBRARIES})
|
||||
else()
|
||||
# Fallback libs commonly required for kcgi on Linux
|
||||
list(APPEND KCGI_LINK_LIBS kcgi z)
|
||||
endif()
|
||||
endif()
|
||||
|
|
@ -42,7 +42,8 @@ foreach(var_pair
|
|||
INIPARSER
|
||||
LIBSQL
|
||||
LIBXML2
|
||||
OPENSSL)
|
||||
OPENSSL
|
||||
KCGI)
|
||||
if(${var_pair}_INCLUDE_DIRS)
|
||||
target_include_directories(fun_core PRIVATE ${${var_pair}_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
|
@ -93,6 +94,10 @@ if(FUN_WITH_OPENSSL)
|
|||
target_compile_definitions(fun_core PUBLIC FUN_WITH_OPENSSL=1)
|
||||
endif()
|
||||
|
||||
if(FUN_WITH_KCGI)
|
||||
target_compile_definitions(fun_core PUBLIC FUN_WITH_KCGI=1)
|
||||
endif()
|
||||
|
||||
# LibreSSL toggle: ensure compile definitions are applied to fun_core so
|
||||
# the LibreSSL code paths are compiled, and linking vars are handled above.
|
||||
|
||||
|
|
|
|||
14
examples/cgi/hello_kcgi.fun
Normal file
14
examples/cgi/hello_kcgi.fun
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* Minimal example using KCGI wrapper.
|
||||
* Run under a CGI/FastCGI environment where kcgi can parse the request.
|
||||
*/
|
||||
|
||||
#include <net/kcgi.fun>
|
||||
|
||||
cgi = KCGI()
|
||||
name = cgi.get_or("name", "Fun")
|
||||
|
||||
body = "<h1>Hello, " + to_string(name) + "!</h1>"
|
||||
cgi.reply(200, "text/html; charset=utf-8", body)
|
||||
46
lib/net/kcgi.fun
Normal file
46
lib/net/kcgi.fun
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*/
|
||||
|
||||
// Thin wrapper around VM kcgi intrinsics
|
||||
|
||||
class KCGI()
|
||||
fun _construct(this)
|
||||
this.req = kcgi_parse()
|
||||
|
||||
fun get(this, key)
|
||||
if (typeof(this.req) == "Map")
|
||||
fields = this.req["fields"]
|
||||
if (typeof(fields) == "Map")
|
||||
return fields[key]
|
||||
return nil
|
||||
|
||||
// Returns the field value if present and non-nil, otherwise returns 'def'.
|
||||
fun get_or(this, key, def)
|
||||
v = this.get(key)
|
||||
if (v == nil)
|
||||
return def
|
||||
// Some code may stringify nil to "nil"; treat that as missing too.
|
||||
if (typeof(v) == "String" && v == "nil")
|
||||
return def
|
||||
return v
|
||||
|
||||
fun reply(this, code, content_type, body)
|
||||
if (kcgi_reply_start(code, content_type) == 1)
|
||||
_ = kcgi_write(to_string(body))
|
||||
_ = kcgi_end()
|
||||
|
||||
// streaming API
|
||||
fun start(this, code, content_type)
|
||||
return kcgi_reply_start(code, content_type)
|
||||
|
||||
fun write(this, chunk)
|
||||
return kcgi_write(to_string(chunk))
|
||||
|
||||
fun end(this)
|
||||
return kcgi_end()
|
||||
1
make
1
make
|
|
@ -29,6 +29,7 @@ if [ "$target" = "all" ]; then
|
|||
-DFUN_WITH_JSON=ON \
|
||||
-DFUN_WITH_INI=ON \
|
||||
-DFUN_WITH_CPP=ON \
|
||||
-DFUN_WITH_KCGI=ON \
|
||||
-DFUN_WITH_OPENSSL=ON \
|
||||
&& cmake --build build --target fun
|
||||
elif [ "$target" = "all_debug" ]; then
|
||||
|
|
|
|||
|
|
@ -143,6 +143,12 @@ typedef enum {
|
|||
OP_PROC_SYSTEM, // pops command string; pushes exit code number
|
||||
OP_TIME_NOW_MS, // pushes current wall-clock time in milliseconds since Unix epoch
|
||||
OP_CLOCK_MONO_MS, // pushes monotonic clock in milliseconds (not wall time)
|
||||
|
||||
// kcgi (optional)
|
||||
OP_KCGI_PARSE, // () -> Map | Nil (parse request via kcgi)
|
||||
OP_KCGI_REPLY_START, // (code:int, content_type:string) -> 1/0
|
||||
OP_KCGI_WRITE, // (chunk:string) -> 1/0
|
||||
OP_KCGI_END, // () -> 1/0 (free request)
|
||||
OP_DATE_FORMAT, // pops fmt string, ms epoch (int); pushes formatted date string using strftime
|
||||
OP_ENV_ALL, // pushes map of all environment variables
|
||||
OP_FUN_VERSION, // pushes version string
|
||||
|
|
|
|||
89
src/extensions/kcgi.c
Normal file
89
src/extensions/kcgi.c
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file kcgi.c
|
||||
* @brief kcgi helpers for Fun VM KCGI-related opcodes (conditional build).
|
||||
*/
|
||||
|
||||
#ifdef FUN_WITH_KCGI
|
||||
|
||||
#include <kcgi.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Thread-local request handle for the current CGI invocation */
|
||||
#ifdef _WIN32
|
||||
static __declspec(thread) struct kreq *g_kcgi_req = NULL;
|
||||
#else
|
||||
static __thread struct kreq *g_kcgi_req = NULL;
|
||||
#endif
|
||||
|
||||
static Value kcgi_fields_to_map(const struct kreq *r) {
|
||||
Value m = make_map_empty();
|
||||
if (!r) return m;
|
||||
for (size_t i = 0; i < r->fieldsz; i++) {
|
||||
const char *k = r->fields[i].key ? r->fields[i].key : "";
|
||||
const char *v = r->fields[i].val ? r->fields[i].val : "";
|
||||
map_set(&m, k, make_string(v));
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
static Value kreq_to_fun(const struct kreq *r) {
|
||||
Value out = make_map_empty();
|
||||
if (!r) return out;
|
||||
/* Basic request info */
|
||||
map_set(&out, "host", make_string(r->host ? r->host : ""));
|
||||
map_set(&out, "port", make_int((int64_t)r->port));
|
||||
map_set(&out, "path", make_string(r->path ? r->path : ""));
|
||||
map_set(&out, "suffix", make_string(r->suffix ? r->suffix : ""));
|
||||
|
||||
Value fields = kcgi_fields_to_map(r);
|
||||
map_set(&out, "fields", fields);
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Lifecycle helpers used by VM opcodes */
|
||||
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));
|
||||
if (!r) return 0;
|
||||
enum kcgi_err er = khttp_parse(r, keys, 0, NULL, 0, 0);
|
||||
if (er != KCGI_OK) { free(r); return 0; }
|
||||
*out = r;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void kcgi_free_request(struct kreq *r) {
|
||||
if (!r) return;
|
||||
khttp_free(r);
|
||||
free(r);
|
||||
}
|
||||
|
||||
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 */
|
||||
if (ctype && *ctype) {
|
||||
if (khttp_head(g_kcgi_req, "Content-Type", "%s", ctype) != KCGI_OK)
|
||||
return 0;
|
||||
}
|
||||
if (khttp_body(g_kcgi_req) != KCGI_OK)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int kcgi_write_str(const char *s) {
|
||||
if (!g_kcgi_req) return 0;
|
||||
if (!s) s = "";
|
||||
size_t n = strlen(s);
|
||||
return khttp_write(g_kcgi_req, s, n) == KCGI_OK;
|
||||
}
|
||||
|
||||
#endif /* FUN_WITH_KCGI */
|
||||
65
src/parser.c
65
src/parser.c
|
|
@ -1351,6 +1351,71 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
|||
free(name);
|
||||
return 1;
|
||||
}
|
||||
/* KCGI intrinsics (optional feature; opcodes are safe no-ops when disabled) */
|
||||
if (strcmp(name, "kcgi_parse") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!consume_char(src, len, pos, ')')) {
|
||||
parser_fail(*pos, "kcgi_parse expects ()");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
bytecode_add_instruction(bc, OP_KCGI_PARSE, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "kcgi_reply_start") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos)) {
|
||||
parser_fail(*pos, "kcgi_reply_start expects (code:int, content_type:string)");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
if (!consume_char(src, len, pos, ',')) {
|
||||
parser_fail(*pos, "kcgi_reply_start expects 2 args");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
if (!emit_expression(bc, src, len, pos)) {
|
||||
parser_fail(*pos, "kcgi_reply_start expects (code:int, content_type:string)");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
if (!consume_char(src, len, pos, ')')) {
|
||||
parser_fail(*pos, "Expected ')' after kcgi_reply_start args");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
bytecode_add_instruction(bc, OP_KCGI_REPLY_START, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "kcgi_write") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos)) {
|
||||
parser_fail(*pos, "kcgi_write expects (chunk:string)");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
if (!consume_char(src, len, pos, ')')) {
|
||||
parser_fail(*pos, "Expected ')' after kcgi_write arg");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
bytecode_add_instruction(bc, OP_KCGI_WRITE, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "kcgi_end") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!consume_char(src, len, pos, ')')) {
|
||||
parser_fail(*pos, "kcgi_end expects ()");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
bytecode_add_instruction(bc, OP_KCGI_END, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "rust_hello") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!consume_char(src, len, pos, ')')) {
|
||||
|
|
|
|||
9
src/vm.c
9
src/vm.c
|
|
@ -72,6 +72,7 @@
|
|||
#include "extensions/pcsc.c"
|
||||
#include "extensions/sqlite.c"
|
||||
#include "extensions/xml2.c"
|
||||
#include "extensions/kcgi.c"
|
||||
|
||||
/* forward declarations for include mapping used in error reporting */
|
||||
extern char *preprocess_includes(const char *src);
|
||||
|
|
@ -1089,6 +1090,14 @@ void vm_run(VM *vm, Bytecode *entry) {
|
|||
#include "vm/curl/post.c"
|
||||
#endif
|
||||
|
||||
/* KCGI ops */
|
||||
#ifdef FUN_WITH_KCGI
|
||||
#include "vm/kcgi/parse.c"
|
||||
#include "vm/kcgi/reply_start.c"
|
||||
#include "vm/kcgi/write.c"
|
||||
#include "vm/kcgi/end.c"
|
||||
#endif
|
||||
|
||||
/* OpenSSL ops (md5/sha256/sha512/ripemd160) */
|
||||
#ifdef FUN_WITH_OPENSSL
|
||||
#include "vm/openssl/md5.c"
|
||||
|
|
|
|||
10
src/vm/kcgi/end.c
Normal file
10
src/vm/kcgi/end.c
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/* KCGI_END */
|
||||
case OP_KCGI_END: {
|
||||
#ifdef FUN_WITH_KCGI
|
||||
if (g_kcgi_req) { kcgi_free_request(g_kcgi_req); g_kcgi_req = NULL; }
|
||||
push_value(vm, make_int(1));
|
||||
#else
|
||||
push_value(vm, make_int(0));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
20
src/vm/kcgi/parse.c
Normal file
20
src/vm/kcgi/parse.c
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/* KCGI_PARSE */
|
||||
case OP_KCGI_PARSE: {
|
||||
#ifdef FUN_WITH_KCGI
|
||||
if (g_kcgi_req) { /* safety: free previous if any */
|
||||
kcgi_free_request(g_kcgi_req);
|
||||
g_kcgi_req = NULL;
|
||||
}
|
||||
struct kreq *r = NULL;
|
||||
if (!kcgi_parse_request(&r)) {
|
||||
push_value(vm, make_nil());
|
||||
break;
|
||||
}
|
||||
g_kcgi_req = r;
|
||||
Value v = kreq_to_fun(r);
|
||||
push_value(vm, v);
|
||||
#else
|
||||
push_value(vm, make_nil());
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
31
src/vm/kcgi/reply_start.c
Normal file
31
src/vm/kcgi/reply_start.c
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* KCGI_REPLY_START */
|
||||
case OP_KCGI_REPLY_START: {
|
||||
#ifdef FUN_WITH_KCGI
|
||||
Value vct = pop_value(vm);
|
||||
Value vcode = pop_value(vm);
|
||||
int code = 200;
|
||||
if (vcode.type == VAL_INT) {
|
||||
code = (int)vcode.i;
|
||||
} else if (vcode.type == VAL_FLOAT) {
|
||||
code = (int)vcode.d;
|
||||
} else if (vcode.type == VAL_STRING) {
|
||||
if (vcode.s) code = atoi(vcode.s);
|
||||
}
|
||||
char *ct = value_to_string_alloc(&vct);
|
||||
free_value(vcode);
|
||||
free_value(vct);
|
||||
if (!ct || ct[0] == '\0') {
|
||||
/* default content type */
|
||||
free(ct);
|
||||
ct = strdup("text/html; charset=utf-8");
|
||||
}
|
||||
int ok = kcgi_reply_start(code, ct);
|
||||
free(ct);
|
||||
push_value(vm, make_int(ok ? 1 : 0));
|
||||
#else
|
||||
Value a = pop_value(vm); free_value(a);
|
||||
Value b = pop_value(vm); free_value(b);
|
||||
push_value(vm, make_int(0));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
15
src/vm/kcgi/write.c
Normal file
15
src/vm/kcgi/write.c
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/* KCGI_WRITE */
|
||||
case OP_KCGI_WRITE: {
|
||||
#ifdef FUN_WITH_KCGI
|
||||
Value vs = pop_value(vm);
|
||||
char *s = value_to_string_alloc(&vs);
|
||||
free_value(vs);
|
||||
int ok = kcgi_write_str(s ? s : "");
|
||||
if (s) free(s);
|
||||
push_value(vm, make_int(ok ? 1 : 0));
|
||||
#else
|
||||
Value drop = pop_value(vm); free_value(drop);
|
||||
push_value(vm, make_int(0));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
|
@ -47,6 +47,7 @@ This section documents Fun's optional, build-time selectable extensions. Each pa
|
|||
- [PCRE2 (Perl-compatible regex)](./pcre2/)
|
||||
- [PC/SC (Smart cards)](./pcsc/)
|
||||
- [OpenSSL](./openssl/)
|
||||
- [kcgi](./kcgi/)
|
||||
|
||||
## Notes:
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ tags:
|
|||
- optional
|
||||
---
|
||||
|
||||
|
||||
- CMake option: FUN_WITH_INI=ON
|
||||
- Purpose: Read/write simple INI configuration files.
|
||||
- Homepage: [https://github.com/ndevilla/iniparser](https://github.com/ndevilla/iniparser){:class="ext"}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ tags:
|
|||
- optional
|
||||
---
|
||||
|
||||
|
||||
- CMake option: FUN_WITH_JSON=ON
|
||||
- Purpose: JSON parse/stringify and file helpers via json-c.
|
||||
- Homepage: [https://json-c.github.io/json-c/](https://json-c.github.io/json-c/){:class="ext"}
|
||||
|
|
|
|||
49
web/documentation/extensions/kcgi/kcgi.md
Normal file
49
web/documentation/extensions/kcgi/kcgi.md
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
layout: page
|
||||
published: true
|
||||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - KCGI extension (optional)
|
||||
subtitle: Documentation for KCGI (kcgi) extension (optional)
|
||||
description: Documentation for KCGI (kcgi) extension (optional)
|
||||
permalink: /documentation/extensions/kcgi/
|
||||
lang: en
|
||||
tags:
|
||||
- extension
|
||||
- kcgi
|
||||
- optional
|
||||
---
|
||||
|
||||
- CMake option: FUN_WITH_KCGI=ON
|
||||
- Purpose: integrate the kcgi (CGI/FastCGI) C library; provide request parsing and response helpers for building CGI apps in Fun.
|
||||
- Homepage: [https://kristaps.bsd.lv/kcgi/](https://kristaps.bsd.lv/kcgi/){:class="ext"}
|
||||
|
||||
## Build notes:
|
||||
|
||||
- Requires system kcgi development headers and libraries (pkg-config name: kcgi).
|
||||
- If pkg-config is not available, build falls back to linking against libkcgi and zlib (as configured in cmake/Extensions/KCGI.cmake).
|
||||
|
||||
## Provided helper/opcodes:
|
||||
|
||||
- Function: kcgi_parse(data: none) -> Map | Nil. Parses the current CGI/FastCGI request via kcgi and returns a Map with keys like method, scheme, host, port, path, suffix, query, and fields (GET/POST map). Returns Nil on failure or when the extension is disabled.
|
||||
- Function: kcgi_reply_start(code:int, content_type:string) -> 1/0. Starts the HTTP reply (sets Content-Type and opens the body).
|
||||
- Function: kcgi_write(text:string) -> 1/0. Writes a chunk to the response body.
|
||||
- Function: kcgi_end() -> 1/0. Finalizes the response and frees request resources.
|
||||
- Opcodes: OP_KCGI_PARSE, OP_KCGI_REPLY_START, OP_KCGI_WRITE, OP_KCGI_END (internal mappings for the functions above).
|
||||
|
||||
## Quickstart:
|
||||
|
||||
- Configure: cmake -S . -B build -DFUN_WITH_KCGI=ON
|
||||
- Build: cmake --build build --target fun
|
||||
- Run example:
|
||||
- FUN_LIB_DIR="$(pwd)/lib" ./build/fun examples/cgi/hello_kcgi.fun
|
||||
|
||||
## Example output:
|
||||
|
||||
- Content-Type: text/html; charset=utf-8
|
||||
- <h1>Hello, Fun!</h1>
|
||||
|
||||
## Notes:
|
||||
|
||||
- Running outside a real CGI/FastCGI environment may emit RFC warnings (e.g., missing REMOTE_ADDR); these are benign for local testing.
|
||||
- When FUN_WITH_KCGI is OFF, the helpers behave as no-ops (returning Nil/0) to keep scripts portable.
|
||||
|
|
@ -15,7 +15,6 @@ tags:
|
|||
- optional
|
||||
---
|
||||
|
||||
|
||||
- CMake option: FUN_WITH_OPENSSL=ON
|
||||
- Purpose: provide small crypto helpers backed by OpenSSL. Includes md5, sha256, sha512, ripemd160 helpers.
|
||||
- Homepage: [https://www.openssl.org/](https://www.openssl.org/){:class="ext"}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ tags:
|
|||
- regex
|
||||
---
|
||||
|
||||
|
||||
- CMake option: FUN_WITH_PCRE2=ON
|
||||
- Purpose: Advanced regular expressions via PCRE2.
|
||||
- Homepage: [https://www.pcre.org/](https://www.pcre.org/){:class="ext"}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ tags:
|
|||
- smart
|
||||
---
|
||||
|
||||
|
||||
- CMake option: FUN_WITH_PCSC=ON
|
||||
- Purpose: Access smart card readers/cards via PC/SC (pcsclite).
|
||||
- Homepage: [https://pcsclite.apdu.fr/](https://pcsclite.apdu.fr/){:class="ext"}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ tags:
|
|||
- sqlite
|
||||
---
|
||||
|
||||
|
||||
- CMake option: FUN_WITH_SQLITE=ON
|
||||
- Purpose: Access SQLite databases via the native C API.
|
||||
- Homepage: [https://www.sqlite.org/](https://www.sqlite.org/){:class="ext"}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ tags:
|
|||
- xml
|
||||
---
|
||||
|
||||
|
||||
- CMake option: FUN_WITH_XML2=ON
|
||||
- Purpose: Minimal XML parsing helpers using libxml2.
|
||||
- Homepage: [http://xmlsoft.org/](http://xmlsoft.org/){:class="ext"}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue