4#define _POSIX_C_SOURCE 200809L
9#if defined(__linux__) && defined(_GNU_SOURCE)
34#include "../../net/http/async_internal.h"
35#include "../../net/http/http_live.h"
36#include "../../net/http/sockopts.h"
37#include "../../net/http/simd_parser.h"
38#include "../../net/websocket/ws_async_internal.h"
40#include "../io/async_debug.h"
47#include <ttak/mem/mem.h>
48#include <ttak/timing/timing.h>
49#include <ttak/net/lattice.h>
50#include <ttak/mols_control.h>
51#include <ttak/priority/scheduler.h>
52#include <ttak/async/sched.h>
66#if defined(CWIST_PLATFORM_HOSTED)
72#ifndef CWIST_BAREMETAL
78#ifndef CWIST_BAREMETAL
80#include <netinet/tcp.h>
84#ifndef CWIST_PLATFORM_NO_PROC
85#include <sys/resource.h>
90#define CWIST_ROUTE_BUCKETS 127
91#define CWIST_STATIC_RETIRE_NS TT_SECOND(5)
99#define CWIST_DEFAULT_FD_LIMIT_TARGET ((rlim_t)1050000)
104static void cwist_app_tune_system(
void) {
105#ifdef CWIST_PLATFORM_NO_PROC
128 "[CWIST] Ignoring invalid CWIST_FD_LIMIT_TARGET=\"%s\" (using default %llu)\n",
142 printf(
"[CWIST] Open file soft limit: %llu\n", (
unsigned long long)
rl.rlim_cur);
147#if defined(CWIST_PLATFORM_HOSTED)
157#if defined(CWIST_PLATFORM_HOSTED)
188 if (!mem)
return NULL;
199 memset(entry, 0,
sizeof(*entry));
220 fprintf(
stderr,
"[StaticMem] Failed to allocate %zu bytes via libttak for %s\n", size,
236 fprintf(
stderr,
"[StaticMem] Short read for %s (expected %zu, got %zu)\n", fs_path,
263 if (!mem || !node)
return;
282 const char *fs_path,
const struct stat *
st,
void *data,
284 if (!mem || !entry || !fs_path || !
st)
return false;
294 entry->
size =
st->st_size;
296 snprintf(entry->
etag,
sizeof(entry->
etag),
"\"%lx-%lx\"", (
unsigned long)
st->st_mtime,
297 (
unsigned long)
st->st_size);
314 if (!mem || !fs_path || !
st)
return false;
316 fprintf(
stderr,
"[StaticMem] Skipping %s (size %zu exceeds capacity)\n", fs_path,
322 fprintf(
stderr,
"[StaticMem] Failed to allocate metadata entry for %s\n", fs_path);
347 if (!mem || !entry || !
st)
return false;
351 (
size_t)
st->st_size);
366 entry->
size =
st->st_size;
368 snprintf(entry->
etag,
sizeof(entry->
etag),
"\"%lx-%lx\"", (
unsigned long)
st->st_mtime,
369 (
unsigned long)
st->st_size);
400 void *ws_async_user_data;
436 const char *relative_ptr;
438} cwist_static_request_info;
441static void cwist_route_table_destroy(cwist_route_table *
table);
442static bool cwist_route_table_insert(cwist_route_table *
table,
const char *path,
const char *name,
453 cwist_static_request_info *
info);
455static void cwist_multiport_destroy_owned_subapps(
cwist_app *
root);
456static void cwist_multiport_unlink_app(
cwist_app *app);
465static bool route_has_params(
const char *path) {
466 if (!path)
return false;
477static size_t cwist_route_hash(
cwist_http_method_t method,
const char *path,
size_t path_len,
478 size_t bucket_count) {
479 const unsigned long long FNV_OFFSET = 1469598103934665603ULL;
480 const unsigned long long FNV_PRIME = 1099511628211ULL;
482 const unsigned char *ptr = (
const unsigned char *)path;
483 for (
size_t i = 0;
i < path_len;
i++) {
487 return (
size_t)(
hash % bucket_count);
491static void cwist_route_entry_release_ctx(cwist_route_entry *entry) {
492 if (entry->ctx_destroy) entry->ctx_destroy(entry->user_ctx);
493 entry->user_ctx =
NULL;
494 entry->ctx_destroy =
NULL;
497static void cwist_route_entry_set_target(cwist_route_entry *entry,
498 const cwist_route_target *
target) {
499 entry->handler =
target->handler;
500 entry->handler_ex =
target->handler_ex;
501 entry->user_ctx =
target->user_ctx;
502 entry->ctx_destroy =
target->ctx_destroy;
503 entry->ws_handler =
target->ws_handler;
504 entry->ws_async_on_message =
NULL;
505 entry->ws_async_user_data =
NULL;
508static cwist_route_entry *cwist_route_entry_create(
const char *path,
const char *name,
510 const cwist_route_target *
target,
513 if (!entry)
return NULL;
516 if (!entry->path || (name && !entry->name)) {
522 entry->path_len =
strlen(entry->path);
523 entry->method = method;
524 cwist_route_entry_set_target(entry,
target);
526 entry->has_params = route_has_params(entry->path);
535static void cwist_route_entry_free(cwist_route_entry *entry) {
537 cwist_route_entry_release_ctx(entry);
549 if (!
table->buckets) {
561static void cwist_route_table_destroy(cwist_route_table *
table) {
563 for (
size_t i = 0;
i <
table->bucket_count;
i++) {
567 cwist_route_entry_free(
curr);
576 cwist_route_entry_free(
param);
590static bool cwist_route_table_insert(cwist_route_table *
table,
const char *path,
const char *name,
594 (
table && path) ? cwist_route_entry_create(path, name, method,
target, opts) :
NULL;
600 if (entry->has_params) {
601 entry->next =
table->param_routes;
602 table->param_routes = entry;
606 size_t idx = cwist_route_hash(method, entry->path, entry->path_len,
table->bucket_count);
610 if (!
curr->has_params &&
curr->method == method &&
strcmp(
curr->path, entry->path) == 0) {
614 cwist_route_entry_release_ctx(
curr);
619 entry->ctx_destroy =
NULL;
620 cwist_route_entry_free(entry);
626 entry->next = *bucket;
635 size_t path_len =
strlen(path);
636 size_t idx = cwist_route_hash(method, path, path_len,
table->bucket_count);
646 if (
curr->method == method) {
649 if (
curr->path_len == path_len) {
655 if (
curr->path_len == path_len &&
memcmp(
curr->path, path, path_len) == 0) {
693static size_t cwist_url_decode(
const char *src,
char *
dst,
size_t dst_size) {
696 if (src[
i] ==
'%' &&
isxdigit((
unsigned char)src[
i + 1]) &&
698 char hex[3] = {src[
i + 1], src[
i + 2],
'\0'};
701 }
else if (src[
i] ==
'+') {
717static bool cwist_path_has_parent_ref(
const char *path) {
718 if (!path)
return false;
719 const char *
cursor = path;
723 char next = *(
cursor + 1);
742static bool cwist_static_match_entry(
const cwist_static_dir *entry,
const char *req_path,
743 const char **relative_ptr,
bool *use_index) {
744 if (!entry || !req_path || req_path[0] ==
'\0')
return false;
747 if (
prefix_len == 1 && entry->url_prefix[0] ==
'/') {
748 if (req_path[0] !=
'/')
return false;
749 if (req_path[1] ==
'\0') {
750 if (use_index) *use_index =
true;
751 if (relative_ptr) *relative_ptr =
NULL;
753 if (use_index) *use_index =
false;
754 if (relative_ptr) *relative_ptr = req_path + 1;
765 if (use_index) *use_index =
true;
766 if (relative_ptr) *relative_ptr =
NULL;
772 if (use_index) *use_index =
false;
773 if (relative_ptr) *relative_ptr = req_path +
prefix_len + 1;
785 cwist_static_request_info *
info) {
786#if !defined(CWIST_PLATFORM_HOSTED)
793 if (!app || !req || !req->
path || !req->
path->
data)
return false;
800 bool use_index =
false;
802 if (cwist_static_match_entry(entry, path, &
relative, &use_index)) {
804 info->mapping = entry;
806 info->use_index = use_index;
816#if defined(CWIST_PLATFORM_HOSTED)
865 fprintf(
stderr,
"[StaticMem] Out of memory; static cache disabled\n");
896 printf(
"Server Memory Initialized: %zu used / %zu total bytes (%zu files)\n",
914 if (
stat(
f->fs_path, &
st) == 0) {
915 if (
st.st_mtime >
f->last_mod) {
917 printf(
"[Hot Reload] Updated: %s\n",
f->fs_path);
928 if (!mem || !fs_path)
return NULL;
938static char *cwist_normalize_prefix(
const char *prefix) {
939 if (!prefix || prefix[0] ==
'\0') {
943 size_t len =
strlen(prefix);
952 memcpy(buffer + 1, prefix, len + 1);
955 memcpy(buffer, prefix, len + 1);
958 while (len > 1 && buffer[len - 1] ==
'/') {
959 buffer[len - 1] =
'\0';
966static char *cwist_normalize_directory(
const char *
directory) {
976 while (len > 1 &&
directory[len - 1] ==
'/') {
980 if (!copy)
return NULL;
986#if defined(CWIST_PLATFORM_HOSTED)
1008 mw_executor_ctx *ctx = (mw_executor_ctx *)req->
private_data;
1009 cwist_static_request_info *
info = ctx ? (cwist_static_request_info *)ctx->handler_data :
NULL;
1017 if (
info->use_index || !
info->relative_ptr ||
info->relative_ptr[0] ==
'\0') {
1072 const char *mime =
"application/octet-stream";
1075 mime =
"text/html; charset=utf-8";
1077 mime =
"text/css; charset=utf-8";
1079 mime =
"application/javascript";
1081 mime =
"application/json";
1085 mime =
"image/jpeg";
1089 mime =
"image/svg+xml";
1091 mime =
"text/plain; charset=utf-8";
1095 const char *etag =
file->etag;
1118 const char *
cc =
info->mapping->cache_control ?
info->mapping->cache_control
1119 :
"public, max-age=3600";
1139 if (
end ==
p + 1 || *
end !=
'\0') {
1195 const char *
cc =
info->mapping->cache_control ?
info->mapping->cache_control
1196 :
"public, max-age=3600";
1220#if defined(CWIST_PLATFORM_HOSTED)
1228 err.error.err_i16 = -1;
1249 app->
use_https2 ? static_ssl_http2_handler : static_ssl_http1_handler;
1258 err.error.err_i16 = -1;
1274 err.error.err_i16 = -1;
1280 err.error.err_i16 = 0;
1294 if (!app)
return NULL;
1305 app->
router = cwist_route_table_create();
1336#if defined(CWIST_PLATFORM_HOSTED)
1351 if (destroy) destroy(user_ctx);
1393 err.error.err_i16 = add_middleware(app,
mw,
mw_ex, user_ctx, destroy) ? 0 : -1;
1417 if (!app || !handler)
return;
1420 if (
curr->status_code == status) {
1436 if (!app)
return NULL;
1439 if (
curr->status_code == status) {
1456 if (!app || !app->
bdr_ctx)
return;
1466#if defined(CWIST_PLATFORM_HOSTED)
1467 cwist_multiport_destroy_owned_subapps(app);
1468 cwist_multiport_unlink_app(app);
1473#if defined(CWIST_PLATFORM_HOSTED)
1480 cwist_route_table_destroy(app->
router);
1515#if defined(CWIST_PLATFORM_HOSTED)
1547#if defined(CWIST_PLATFORM_HOSTED) && defined(CWIST_DB)
1576#if defined(CWIST_PLATFORM_HOSTED)
1600 mw_executor_ctx *ctx = (mw_executor_ctx *)req->
private_data;
1603 if (ctx->current_mw_node) {
1606 ctx->current_mw_node = node->
next;
1610 node->
func(req, res, mw_next_wrapper);
1612 }
else if (ctx->final_handler) {
1613 ctx->final_handler(req, res);
1627 mw_executor_ctx ctx = {app->
middlewares, final_handler, handler_data};
1632 if (final_handler) final_handler(req, res);
1634 mw_next_wrapper(req, res);
1647#if !defined(CWIST_PLATFORM_HOSTED)
1651 err.error.err_i16 = -1;
1656 if (!app || !cert_path || !key_path) {
1657 err.error.err_i16 = -1;
1676 err.error.err_i16 = -1;
1692#if !defined(CWIST_PLATFORM_HOSTED)
1696 err.error.err_i16 = -1;
1701 if (!app || !cert_path || !key_path || !app->
use_ssl) {
1702 err.error.err_i16 = -1;
1706 err.error.err_i16 = -1;
1711 if (!cwist_error_is_ok(&
err)) {
1720 if (!cwist_error_is_ok(&
err)) {
1733 err.error.err_i16 = -1;
1763 err.error.err_i16 = -1;
1768#if !defined(CWIST_PLATFORM_HOSTED)
1769 err.error.err_i16 = -1;
1773 err.error.err_i16 = 0;
1786 err.error.err_i16 = -1;
1791#if !defined(CWIST_PLATFORM_HOSTED) || !defined(CWIST_HTTP3)
1792 err.error.err_i16 = -1;
1795 err.error.err_i16 = 0;
1809 err.error.err_i16 = -1;
1814 err.error.err_i16 = 0;
1821 err.error.err_i16 = -1;
1826#if !defined(CWIST_PLATFORM_HOSTED) || !defined(CWIST_HTTP3)
1827 err.error.err_i16 = -1;
1830 err.error.err_i16 = 0;
1838#if defined(CWIST_PLATFORM_HOSTED) && defined(CWIST_HTTP3)
1853 if (!app || !db_path) {
1854 err.error.err_i16 = -1;
1858#if !defined(CWIST_PLATFORM_HOSTED) || !defined(CWIST_DB)
1865 if (
err.error.err_i16 < 0) {
1892 if (!app || !db_path) {
1893 err.error.err_i16 = -1;
1897#if !defined(CWIST_PLATFORM_HOSTED) || !defined(CWIST_DB)
1898 (
void)sync_interval_ms;
1907 "[CWIST] Nuke DB disabled for '%s' (insufficient RAM). Falling back to standard SQLite.\n",
1911 err.error.err_i16 = -1;
1931 err.error.err_i16 = -1;
1938 err.error.err_i16 = 0;
1949 if (!app)
return NULL;
1950#if defined(CWIST_PLATFORM_HOSTED) && defined(CWIST_DB)
1960 if (!app || !db_path || max_conns == 0) {
1961 err.error.err_i16 = -1;
1964#if !defined(CWIST_PLATFORM_HOSTED) || !defined(CWIST_DB)
1976 err.error.err_i16 = -1;
1979 err.error.err_i16 = 0;
1985 if (!app)
return NULL;
1991 if (!app || !host || port <= 0 || max_conns == 0) {
1992 err.error.err_i16 = -1;
1995#if !defined(CWIST_PLATFORM_HOSTED)
1999 err.error.err_i16 = -1;
2007 err.error.err_i16 = -1;
2010 err.error.err_i16 = 0;
2016 if (!app)
return NULL;
2022 if (!app || worker_count == 0) {
2023 err.error.err_i16 = -1;
2026#if !defined(CWIST_PLATFORM_HOSTED)
2029 err.error.err_i16 = -1;
2037 err.error.err_i16 = -1;
2040 err.error.err_i16 = 0;
2046 if (!app)
return NULL;
2059 if (!app || !url_prefix || !
directory) {
2060 err.error.err_i16 = -1;
2064 char *
normalized = cwist_normalize_prefix(url_prefix);
2066 err.error.err_i16 = -1;
2073 err.error.err_i16 = -1;
2081 err.error.err_i16 = -1;
2087 entry->cache_control =
NULL;
2091 err.error.err_i16 = 0;
2104 const char *
directory,
const char *cache_control) {
2106 if (!app || !url_prefix || !
directory || !cache_control) {
2107 err.error.err_i16 = -1;
2111 char *
normalized = cwist_normalize_prefix(url_prefix);
2113 err.error.err_i16 = -1;
2120 err.error.err_i16 = -1;
2128 err.error.err_i16 = -1;
2135 if (!entry->cache_control) {
2139 err.error.err_i16 = -1;
2145 err.error.err_i16 = 0;
2149static void add_route_named(
cwist_app *app,
const char *path,
const char *name,
2152 if (!app || !app->
router || !path)
return;
2156 const cwist_route_target
target = {.handler = handler};
2157 cwist_route_table_insert(app->
router, path, name, method, &
target, opts);
2166 if (!app || !app->
router || !handler) {
2167 if (destroy) destroy(user_ctx);
2168 err.error.err_i16 = -1;
2171 const cwist_route_target
target = {
2172 .handler_ex = handler, .user_ctx = user_ctx, .ctx_destroy = destroy};
2176 err.error.err_i16 = ok ? 0 : -1;
2182 return add_route_ex(app, path,
CWIST_HTTP_GET, handler, user_ctx, destroy);
2187 return add_route_ex(app, path,
CWIST_HTTP_POST, handler, user_ctx, destroy);
2192 return add_route_ex(app, path,
CWIST_HTTP_PUT, handler, user_ctx, destroy);
2202 return add_route_ex(app, path,
CWIST_HTTP_PATCH, handler, user_ctx, destroy);
2207 add_route_named(app, path,
NULL, method, handler, opts);
2229 cwist_app_get(app,
"/metrics", cwist_metrics_route_handler);
2251 cwist_app_get(app,
"/healthz", cwist_healthz_route_handler);
2253 cwist_app_get(app,
"/ready", cwist_readiness_route_handler);
2311 if (!app || !app->
router || !path)
return;
2312 const cwist_route_target
target = {.ws_handler = handler};
2333 if (!app || !app->
router || !path || !on_message)
return;
2334 const cwist_route_target
target = {0};
2339 entry->ws_async_on_message = on_message;
2340 entry->ws_async_user_data = user_data;
2353 return cwist_endpoint_has(opts,
flag);
2405 if (!app || !app->
router || !path)
return;
2409 const cwist_route_target
target = {.ws_handler = handler};
2425 const char *
p = pattern;
2428 while (*
p ==
'/')
p++;
2429 while (*
a ==
'/')
a++;
2430 if (*
p ==
'\0' || *
a ==
'\0')
return *
p ==
'\0' && *
a ==
'\0';
2439 char key[256],
val[256];
2460 for (
size_t i = 0;
i <
table->bucket_count;
i++) {
2476 if (!app || !app->
router || !name)
return NULL;
2478 if (!entry)
return NULL;
2479 const char *path = entry->path;
2480 if (!
params || !entry->has_params) {
2485 if (!result)
return NULL;
2495 if (
tok[0] ==
':') {
2517 if (!app || !req || !res)
return;
2520 internal_route_handler(app, req, res);
2530 if (!req)
return -1;
2556#define CWIST_STREAM_REQ_EAGER_MAX ((size_t)1 << 20)
2558struct cwist_stream_req {
2564 size_t content_length;
2572static long cwist_stream_parse_content_length(
const char *
head,
size_t head_len) {
2573 static const char cl_name[] =
"content-length:";
2574 const char *
p =
head;
2575 const char *
end =
head + head_len;
2579 unsigned char c = (
unsigned char)
p[
i];
2580 if (
c >=
'A' &&
c <=
'Z')
c = (
unsigned char)(
c + (
'a' -
'A'));
2585 const char *
v =
p +
i;
2586 while (
v <
end && (*
v ==
' ' || *
v ==
'\t'))
v++;
2594 while (
p <
end && *
p !=
'\n')
p++;
2601 if (!
head || head_len == 0)
return NULL;
2603 if (!r)
return NULL;
2604 long cl = cwist_stream_parse_content_length(
head, head_len);
2609 r->head_len = head_len;
2615 r->wire_cap = head_len +
eager;
2622 r->complete =
false;
2627 if (!r || (!
chunk && len > 0))
return -1;
2628 if (len > r->content_length - r->body_len)
return -1;
2629 if (len == 0)
return 0;
2630 size_t used = r->head_len + r->body_len;
2631 if (len > r->wire_cap - used) {
2632 size_t ncap = r->wire_cap ? r->wire_cap : 4096;
2633 while (
ncap - r->head_len < r->body_len + len)
ncap *= 2;
2648 if (r->body_len != r->content_length)
return -1;
2657 if (!app || !
write_fn || !r->complete) {
2673 if (!req)
return -1;
2707 if (head_len == 0 || head_len >=
sizeof(
head_buf)) {
2717 size_t body_len = 0;
2727 while (off < body_len) {
2728 size_t n = body_len - off;
2745 mw_executor_ctx *ctx = (mw_executor_ctx *)req->
private_data;
2752 mw_executor_ctx *ctx = (mw_executor_ctx *)req->
private_data;
2758 cwist_route_entry *
route) {
2759 if (
route->handler_ex) {
2760 execute_chain(app, req, res, cwist_route_ex_handler,
route);
2762 execute_chain(app, req, res,
route->handler,
NULL);
2768 if (!req || !app || !app->
router)
return;
2783#if defined(CWIST_PLATFORM_HOSTED)
2819#if !defined(CWIST_PLATFORM_HOSTED)
2869 execute_chain(app, req, res, cwist_asset_handler, &
asset_match);
2874 if (cwist_prepare_static(app, req, &
static_info)) {
2875#if defined(CWIST_PLATFORM_HOSTED)
2878 execute_chain(app, req, res, cwist_static_handler, &
static_info);
2897#if !defined(__EMSCRIPTEN__) && !defined(CWIST_PLATFORM_NO_SOCKETS)
2903 if (!app || !req || !res)
return;
2906 internal_route_handler(app, req, res);
2913 if (!app || !req || !res)
return;
2916 internal_route_handler(app, req, res);
2923#ifndef CWIST_PLATFORM_NO_PROC
2929 if (!app || !conn)
return;
2932 static_ssl_http2_handler(conn, ctx);
2936 static_ssl_http1_handler(conn, ctx);
2973 internal_route_handler(app, req, res);
2998 bool detached =
false;
3010 if (!keep_alive || upgraded)
return;
3017 static_ssl_http1_handler(conn, ctx);
3045 int status = app_parse_error_status(
perr);
3055 int status = app_parse_error_status(
perr);
3067 int d = cwist_async_debug_enabled();
3072 if (
n <= 5 ||
n % 10000 == 0)
3073 fprintf(
stderr,
"[async] flush-parked fd=%d total=%ld\n", client_fd,
n);
3092#ifndef CWIST_PLATFORM_NO_SOCKETS
3109 &
aconn->bdr_cursor);
3159 internal_route_handler(app, req, res);
3276#define CWIST_HTTP_REQUESTS_PER_TURN_DEFAULT 16
3277#define CWIST_HTTP_REQUESTS_PER_TURN_MAX 1024
3281static unsigned int app_http_requests_per_turn(
void) {
3282 static _Atomic
int cached = -1;
3285 const char *
env =
getenv(
"CWIST_HTTP_BATCH");
3292 return (
unsigned int)
v;
3300static unsigned int app_http_yield_batch(
void) {
3301 static _Atomic
int cached = -1;
3304 const char *
env =
getenv(
"CWIST_HTTP_YIELD_BATCH");
3311 return (
unsigned int)
v;
3314#ifndef CWIST_PLATFORM_NO_SOCKETS
3318#define CWIST_H2C_THREAD_STACK_SIZE (256 * 1024)
3327static void *h2c_handoff_main(
void *arg) {
3328 h2c_handoff_t *h = (h2c_handoff_t *)arg;
3331 .read_buf = h->replay,
3332 .buf_len = h->replay_len,
3333 .negotiated_http2 =
true,
3354 const bool dbg = (
d != 0);
3359 if (
n <= 5 ||
n % 10000 == 0)
3361 "[async] fill-fail fd=%d total=%ld fatal=%ld serve=%ld errno=%d len=%zu\n",
3376 memcmp(conn->
rbuf,
"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24) == 0) {
3407 .buf_len = conn->
len,
3408 .negotiated_http2 =
true,
3427 const unsigned int yield_batch = app_http_yield_batch();
3434 return app_async_flush_exit(client_fd, conn,
3439 app_maybe_send_parse_error_async(conn,
perr);
3442 if (
n <= 5 ||
n % 10000 == 0)
3443 fprintf(
stderr,
"[async] recv-fatal fd=%d total=%ld len=%zu\n", client_fd,
n,
3468 if (
n <= 5 ||
n % 10000 == 0)
3469 fprintf(
stderr,
"[async] serve-close fd=%d total=%ld\n", client_fd,
n);
3479 if (conn->
len > 0) {
3486#ifdef CWIST_PFC_TESTING
3500 return app_async_flush_exit(
3505#ifndef CWIST_PLATFORM_NO_SOCKETS
3515 .negotiated_http2 =
true,
3549 ssize_t bytes =
recv(client_fd, read_buf,
sizeof(read_buf) - 1, 0);
3557 read_buf[buf_len] =
'\0';
3560 char *
hdr_end = (
char *)cwist_simd_find_crlfcrlf(read_buf, buf_len);
3561 if (
hdr_end && (read_buf[0] ==
'G' && read_buf[1] ==
'E' && read_buf[2] ==
'T' &&
3562 read_buf[3] ==
' ')) {
3586 read_buf[buf_len] =
'\0';
3604 app_maybe_send_parse_error(client_fd,
perr);
3627#if defined(CWIST_PLATFORM_HOSTED)
3629#ifndef CWIST_MULTIPORT_MAX_PORTS
3630#define CWIST_MULTIPORT_MAX_PORTS 64
3634 unsigned short port;
3641 unsigned short port;
3669 memset(&result, 0,
sizeof(result));
3670 result.
valid =
false;
3675 if (!ports &&
count > 0) {
3679 for (
size_t i = 0;
i <
count;
i++) {
3680 if (ports[
i] == 0) {
3686 result.
valid =
true;
3730 unsigned short port) {
3734 if (
slot->port == port)
return slot;
3744static void cwist_multiport_unlink_app(
cwist_app *app) {
3751 if (
slot->app == app) {
3768 for (
size_t i = 0;
i <
group->h3_listener_count;
i++) {
3792 group->h3_listener_count = 0;
3799static void cwist_multiport_destroy_owned_subapps(
cwist_app *
root) {
3837 cwist_route_target
target = {.handler = entry->handler,
3838 .handler_ex = entry->handler_ex,
3839 .user_ctx = entry->user_ctx,
3840 .ctx_destroy =
NULL,
3841 .ws_handler = entry->ws_handler};
3846 return !entry->ws_handler && !entry->ws_async_on_message;
3852 for (
size_t i = 0;
i <
table->bucket_count;
i++) {
3853 for (cwist_route_entry *entry =
table->buckets[
i]; entry; entry = entry->next) {
3857 for (cwist_route_entry *entry =
table->param_routes; entry; entry = entry->next) {
3863 if (!src)
return cwist_route_table_create();
3867 for (
size_t i = 0;
i < src->bucket_count;
i++) {
3868 for (cwist_route_entry *entry = src->buckets[
i]; entry; entry = entry->next) {
3870 cwist_route_table_insert(
dst, entry->path, entry->name, entry->method, &
target,
3874 for (cwist_route_entry *entry = src->param_routes; entry; entry = entry->next) {
3876 cwist_route_table_insert(
dst, entry->path, entry->name, entry->method, &
target,
3923 tail = &entry->
next;
3942 entry->cache_control = src->cache_control ?
cwist_strdup(src->cache_control) :
NULL;
3944 if (!entry->url_prefix || !entry->fs_root ||
3945 (src->cache_control && !entry->cache_control)) {
3953 tail = &entry->next;
3965 if (!src)
return NULL;
3974 cwist_route_table_destroy(
dst->router);
3975 dst->router = router;
4011 cwist_multiport_h3_copy_tunables(
dst->h3_ctx, src->
h3_ctx);
4028 if (!
root || port == 0 || port == (
unsigned short)
root->port)
return NULL;
4049 slot->detached =
true;
4062 if (!client)
return NULL;
4069 if (!ports || !
count || port == 0)
return -1;
4070 for (
size_t i = 0;
i < *
count;
i++) {
4071 if (ports[
i] == port)
return 0;
4074 ports[*
count] = port;
4081 if (flags < 0)
return -1;
4087 for (
size_t i = 0;
i <
count;
i++) {
4088 if (
pfds[
i].fd >= 0) {
4102 payload->client_fd = client_fd;
4122 for (
size_t i = 0;
i < ports.
count;
i++) {
4123 if (ports.
ports[
i] == port)
return true;
4136 unsigned short port) {
4151 if (!
dst || !src)
return;
4175 err.error.err_i16 = -1;
4182 err.error.err_i16 = -1;
4189 err.error.err_i16 = 0;
4193 if (
err.error.err_i16 == 0 && app->
h3_ctx && *out) {
4194 cwist_multiport_h3_copy_tunables(*out, app->
h3_ctx);
4206 if (udp_fd < 0)
return -1;
4221 int rcvbuf = 2 * 1024 * 1024;
4222 int sndbuf = 2 * 1024 * 1024;
4256 unsigned short port) {
4289 group->h3_listener_count++;
4293 printf(
"CWIST multiport HTTP/3 fan-out listening on UDP port %hu\n", port);
4332 if (!app)
return -1;
4334 fprintf(
stderr,
"Port %hu invalid: cleartext HTTP/2 cannot share a TLS listener.\n", port);
4338 fprintf(
stderr,
"Port %hu invalid: HTTPS/2 requires cwist_app_use_https.\n", port);
4342 fprintf(
stderr,
"Port %hu invalid: SSL enabled but context not initialized.\n", port);
4347 "Port %hu invalid: ephemeral HTTP/3 and TLS HTTP/3 cannot both be enabled.\n",
4352 fprintf(
stderr,
"Port %hu invalid: HTTPS/3 requires certificate and key paths.\n", port);
4384 cwist_app_tune_system();
4391 if (!
group)
return -1;
4396 fprintf(
stderr,
"cwist_multiport_get_app cannot detach the public/default port %hu.\n",
4403 "Detached multiport sub-app port %hu is not present in the multiport descriptor.\n",
4411 "Assertion failed: Cannot use cleartext HTTP/2 and HTTPS on the same port.\n");
4417 "Assertion failed: Cannot use HTTPS/2 without configuring SSL via cwist_app_use_https.\n");
4421 fprintf(
stderr,
"SSL enabled but context not initialized.\n");
4439 for (
size_t i = 0;
i < ports.
count;
i++) {
4441 fprintf(
stderr,
"cwist_app_multiport supports up to %d total ports.\n",
4459 pfds[
i].revents = 0;
4466 perror(
"Failed to bind multiport listener");
4471 perror(
"Failed to set multiport listener non-blocking");
4481 fprintf(
stderr,
"Failed to initialize multiport HTTP/3 fan-out.\n");
4487 fprintf(
stderr,
"Failed to initialize HTTPS worker pool.\n");
4494 printf(
"CWIST App running on %zu TCP ports via multiport facade (SSL: %s)\n",
port_count,
4501 perror(
"cwist_app_multiport poll");
4504 if (ready == 0)
continue;
4513 if (client_fd < 0) {
4549 printf(
"[CWIST] Multiport facade shutdown complete.\n");
4554#if defined(CWIST_PLATFORM_HOSTED)
4596 setenv(
"CWIST_C1M_MODE",
"1", 0);
4597 setenv(
"CWIST_REACTOR_DRAIN_CHUNK",
"8", 0);
4602 setenv(
"CWIST_C1M_MODE",
"1", 0);
4603 setenv(
"CWIST_MALLOC_ARENA_MAX",
"1", 0);
4604 setenv(
"CWIST_REACTOR_DRAIN_CHUNK",
"8", 0);
4605 setenv(
"CWIST_HTTP_BATCH",
"64", 0);
4606 printf(
"[CWIST] profile: performance\n");
4608 setenv(
"CWIST_C1M_MODE",
"1", 0);
4609 setenv(
"CWIST_MALLOC_ARENA_MAX",
"1", 0);
4610 setenv(
"CWIST_WORKERS",
"2", 0);
4611 printf(
"[CWIST] profile: lowmem\n");
4613 setenv(
"CWIST_C1M_MODE",
"0", 0);
4614 printf(
"[CWIST] profile: lowlat\n");
4617 setenv(
"CWIST_C1M_MODE",
"1", 0);
4618 setenv(
"CWIST_REACTOR_DRAIN_CHUNK",
"8", 0);
4632#if !defined(__EMSCRIPTEN__) && !defined(CWIST_PLATFORM_NO_SOCKETS)
4637#if defined(__EMSCRIPTEN__) || defined(CWIST_PLATFORM_NO_SOCKETS)
4641 if (app) app->
port = port;
4644#ifndef CWIST_PLATFORM_NO_PROC
4658#if !defined(__EMSCRIPTEN__) && !defined(CWIST_PLATFORM_NO_SOCKETS)
4662 cwist_app_tune_system();
4664 if (!app)
return -1;
4671 "Assertion failed: Cannot use cleartext HTTP/2 and HTTPS on the same port.\n");
4678 "Assertion failed: Cannot use HTTPS/2 without configuring SSL via cwist_app_use_https.\n");
4686 "Assertion failed: Cannot use both ephemeral HTTP/3 and TLS HTTP/3 simultaneously on the same port.\n");
4698 perror(
"Failed to bind port");
4706#ifndef CWIST_PLATFORM_NO_PROC
4723 int rcvbuf = 2 * 1024 * 1024;
4724 int sndbuf = 2 * 1024 * 1024;
4729 perror(
"Failed to bind UDP port for HTTP/3");
4757 if (workers < 1) workers = 1;
4759#if defined(__GLIBC__)
4798#ifndef CWIST_PLATFORM_NO_PROC
4804 size_t worker_count = 0;
4805#ifndef CWIST_PLATFORM_NO_PROC
4807 for (
int i = 1;
i < workers;
i++) {
4813 }
else if (
pid < 0) {
4814 perror(
"fork worker failed");
4821#if defined(__linux__) && defined(_GNU_SOURCE)
4825 perror(
"worker CPU affinity");
4847#ifndef CWIST_PLATFORM_NO_PROC
4862 h3_p->udp_fd = udp_fd;
4867 printf(
"HTTP/3 (QUIC) enabled on UDP port %d\n", port);
4880 printf(
"CWIST App running on port %d (SSL: %s) [Event-driven, workers=%d, pid=%d]\n", port,
4884 const char *
c1m =
getenv(
"CWIST_C1M_MODE");
4885#ifdef CWIST_PLATFORM_NO_PROC
4897#ifndef CWIST_PLATFORM_NO_PROC
4905#ifdef CWIST_PLATFORM_NO_PROC
4907 fprintf(
stderr,
"TLS is not available on WASI (no BoringSSL); serve cleartext.\n");
4915 .
use_forking =
false, .use_threading =
false, .use_epoll =
false};
4920 fprintf(
stderr,
"SSL enabled but context not initialized.\n");
4927 .
use_forking =
false, .use_threading =
true, .use_epoll =
false};
4940#ifndef CWIST_PLATFORM_NO_PROC
4969#ifndef CWIST_PLATFORM_NO_PROC
4974 for (
size_t i = 0;
i < worker_count;
i++) {
4977 for (
size_t i = 0;
i < worker_count;
i++) {
4984 perror(
"waitpid worker");
4998 printf(
"[CWIST] Shutdown complete.\n");
5004static char cwist_swagger_json_path[512] =
"openapi.json";
5008 static const char html[] =
5009 "<!DOCTYPE html>\n<html>\n<head>\n"
5010 "<title>CWIST Swagger UI</title>\n"
5011 "<link rel=\"stylesheet\" href=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui.css\" />\n"
5013 "<div id=\"swagger-ui\"></div>\n"
5014 "<script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js\"></script>\n"
5016 "window.onload = () => {\n"
5017 " SwaggerUIBundle({\n"
5018 " url: '/openapi.json',\n"
5019 " dom_id: '#swagger-ui',\n"
5023 "</body>\n</html>\n";
5030 FILE *
f =
fopen(cwist_swagger_json_path,
"rb");
5040 char *buf = (
char *)
malloc((
size_t)
sz + 1);
5059 cwist_app_get(app,
"/openapi.json", cwist_swagger_json_handler);
Memory allocation wrappers backed by libttak.
void cwist_free(void *ptr)
Free memory obtained via cwist_alloc/cwist_strdup/etc.
void * cwist_realloc(void *ptr, size_t new_size)
Resize an existing allocation (libttak tracked).
char * cwist_strdup(const char *src)
Duplicate a string using libttak-backed storage.
void * cwist_alloc_array(size_t count, size_t elem_size)
Allocate zeroed memory for count elements.
void * cwist_alloc(size_t size)
Allocate zeroed memory tracked by libttak.
int cwist_app_listen(cwist_app *app, int port)
Initialize runtime services and enter the HTTP or HTTPS server loop.
void cwist_app_delete(cwist_app *app, const char *path, cwist_handler_func handler)
void cwist_app_enable_metrics(cwist_app *app)
app_serve_result_t
Route and respond to one fully parsed HTTP/1.1 request. Shared by the blocking keep-alive loop and th...
void cwist_app_enable_swagger(cwist_app *app, const char *mount_path, const char *openapi_json_path)
Enables embedded Swagger UI interactive documentation page.
cwist_error_t cwist_app_static_with_cache(cwist_app *app, const char *url_prefix, const char *directory, const char *cache_control)
Register a filesystem directory with a custom Cache-Control header.
cwist_error_t cwist_app_use_ex(cwist_app *app, cwist_middleware_func mw, cwist_middleware_func_ex mw_ex, void *user_ctx, cwist_middleware_ctx_destroy_func destroy)
Append an extended middleware callback to the application's execution chain.
void cwist_app_put_named(cwist_app *app, const char *path, const char *name, cwist_handler_func handler)
void cwist_app_delete_opt(cwist_app *app, const char *path, cwist_handler_func handler, cwist_endpoint_opt_t opts)
cwist_stream_req_t * cwist_stream_req_begin(const char *head, size_t head_len)
Begin an incremental request: head is the serialized request line plus headers (everything up to and ...
void cwist_app_ws_opt(cwist_app *app, const char *path, cwist_ws_handler_func handler, cwist_endpoint_opt_t opts)
Register a WebSocket route with explicit endpoint options.
cwist_error_t cwist_app_use_nuke_db(cwist_app *app, const char *db_path, int sync_interval_ms)
Enable NUKE DB and fall back to standard SQLite when RAM bootstrap fails.
cwist_error_t cwist_app_static(cwist_app *app, const char *url_prefix, const char *directory)
Register a filesystem directory to be served beneath a URL prefix.
#define CWIST_HTTP_REQUESTS_PER_TURN_DEFAULT
Event-driven HTTP/1.1 handler for the C1M reactor path. Drains the socket without blocking and serves...
#define CWIST_STATIC_RETIRE_NS
void cwist_app_post_named(cwist_app *app, const char *path, const char *name, cwist_handler_func handler)
int cwist_stream_req_end(cwist_stream_req_t *r)
Declare the body complete. The fed byte count must equal the declared Content-Length (or both be zero...
void cwist_app_get(cwist_app *app, const char *path, cwist_handler_func handler)
Register a GET handler with default endpoint options.
void cwist_apply_profile(void)
Apply a CWIST_PROFILE preset before the per-variable env reads in cwist_app_listen()....
#define CWIST_ROUTE_BUCKETS
void cwist_app_http_handler(int client_fd, void *ctx)
char * cwist_url_for(cwist_app *app, const char *name, cwist_query_map *params)
void cwist_app_configure_bdr(cwist_app *app, size_t max_bytes, time_t max_entry_age_sec, uint64_t revalidate_hits)
Configure the Big Dumb Reply cache thresholds for the application.
#define CWIST_DEFAULT_FD_LIMIT_TARGET
cwist_db_pool_t * cwist_app_get_db_pool(cwist_app *app)
Return the application's database connection pool.
cwist_error_t cwist_app_reload_tls(cwist_app *app, const char *cert_path, const char *key_path)
Hot-reload the TLS certificate pair on every live TLS context of the application. See the public cont...
void cwist_app_patch_named(cwist_app *app, const char *path, const char *name, cwist_handler_func handler)
cwist_error_t cwist_app_use_redis(cwist_app *app, const char *host, int port, size_t max_conns)
Configure a Redis connection pool for the application.
void cwist_app_post(cwist_app *app, const char *path, cwist_handler_func handler)
Register a POST handler with default endpoint options.
#define CWIST_HTTP_REQUESTS_PER_TURN_MAX
cwist_error_t cwist_app_use_db(cwist_app *app, const char *db_path)
Open a SQLite database and attach it as the shared application handle.
void cwist_app_put(cwist_app *app, const char *path, cwist_handler_func handler)
int cwist_app_dispatch_stream(cwist_app *app, const char *req_buf, size_t req_len, cwist_stream_write_fn write_fn, void *write_ctx)
Like cwist_app_dispatch_memory(), but streams the serialized response through write_fn instead of ret...
void cwist_app_get_opt(cwist_app *app, const char *path, cwist_handler_func handler, cwist_endpoint_opt_t opts)
Register a GET handler with explicit endpoint options.
cwist_error_t cwist_app_post_ex(cwist_app *app, const char *path, cwist_handler_ex_func handler, void *user_ctx, cwist_handler_ctx_destroy_func destroy)
POST counterpart of cwist_app_get_ex().
bool cwist_endpoint_has_extern(cwist_endpoint_opt_t opts, cwist_endpoint_opt_t flag)
Out-of-line cwist_endpoint_has(), for callers that cannot use a static inline function,...
cwist_error_t cwist_app_put_ex(cwist_app *app, const char *path, cwist_handler_ex_func handler, void *user_ctx, cwist_handler_ctx_destroy_func destroy)
PUT counterpart of cwist_app_get_ex().
void cwist_app_set_error_handler(cwist_app *app, cwist_error_handler_func handler)
Install a custom HTTP error callback.
cwist_error_t cwist_app_get_ex(cwist_app *app, const char *path, cwist_handler_ex_func handler, void *user_ctx, cwist_handler_ctx_destroy_func destroy)
Registers a GET route whose handler receives a user context.
void cwist_app_post_opt(cwist_app *app, const char *path, cwist_handler_func handler, cwist_endpoint_opt_t opts)
Register a POST handler with explicit endpoint options.
struct cwist_route_entry cwist_route_entry
void cwist_app_set_tls_groups(cwist_app *app, const char *groups)
Override TLS groups explicitly.
void cwist_app_patch_opt(cwist_app *app, const char *path, cwist_handler_func handler, cwist_endpoint_opt_t opts)
#define CWIST_STREAM_REQ_EAGER_MAX
void cwist_app_use(cwist_app *app, cwist_middleware_func mw)
Append a middleware callback to the application's execution chain.
cwist_error_t cwist_app_use_db_pool(cwist_app *app, const char *db_path, size_t max_conns)
Open a SQLite connection pool and attach it to the application.
void cwist_app_get_named(cwist_app *app, const char *path, const char *name, cwist_handler_func handler)
void cwist_app_destroy(cwist_app *app)
Release every resource owned by the application object.
int cwist_stream_req_feed(cwist_stream_req_t *r, const char *chunk, size_t len)
Feed one body chunk. Chunks may be any size and may split anywhere; reassembly is the implementation'...
void cwist_app_enable_healthz(cwist_app *app)
Register built-in health check routes on an application.
void cwist_app_put_opt(cwist_app *app, const char *path, cwist_handler_func handler, cwist_endpoint_opt_t opts)
cwist_error_t cwist_app_use_http2(cwist_app *app, bool enabled)
Enable HTTP/2 cleartext (h2c) over standard TCP.
cwist_scheduler_t * cwist_app_get_scheduler(cwist_app *app)
Return the application's background job scheduler.
cwist_app * cwist_app_create(void)
Creates a new CWIST application instance.
#define CWIST_H2C_THREAD_STACK_SIZE
cwist_error_t cwist_app_patch_ex(cwist_app *app, const char *path, cwist_handler_ex_func handler, void *user_ctx, cwist_handler_ctx_destroy_func destroy)
PATCH counterpart of cwist_app_get_ex().
cwist_error_t cwist_app_use_http3(cwist_app *app, bool enabled)
Enable HTTP/3 (QUIC) without manual SSL configuration. Auto-generates an ephemeral self-signed certif...
void cwist_app_use_webtransport(cwist_app *app, cwist_webtransport_handler_func handler)
Enable WebTransport over HTTP/3 on the application.
cwist_error_t cwist_app_use_https2(cwist_app *app, bool enabled)
void cwist_app_dispatch(cwist_app *app, cwist_http_request *req, cwist_http_response *res)
Dispatch a request internally through the app's router and middleware. Used by the test client and ad...
void cwist_app_ws_async(cwist_app *app, const char *path, cwist_ws_on_message_t on_message, void *user_data)
Register a callback-shaped non-blocking WebSocket endpoint (C1M mode).
cwist_async_action_t cwist_app_http_handler_async(int client_fd, cwist_http_async_conn_t *conn)
int cwist_app_listen_ex(cwist_app *app, int port, int workers_override, int c1m_override)
cwist_app_listen() with the worker count and server mode given explicitly.
void cwist_app_patch(cwist_app *app, const char *path, cwist_handler_func handler)
void cwist_app_register_error_handler(cwist_app *app, cwist_http_status_t status, cwist_error_handler_func handler)
int cwist_stream_req_dispatch(cwist_stream_req_t *r, cwist_app *app, cwist_stream_write_fn write_fn, void *write_ctx)
Dispatch a completed incremental request with a streaming response. Takes ownership of req regardless...
void cwist_app_set_max_memspace(cwist_app *app, size_t size)
Override the static file memory budget used by cwist_mem_init().
cwist_error_t cwist_app_delete_ex(cwist_app *app, const char *path, cwist_handler_ex_func handler, void *user_ctx, cwist_handler_ctx_destroy_func destroy)
DELETE counterpart of cwist_app_get_ex().
cwist_error_t cwist_app_use_scheduler(cwist_app *app, size_t worker_count, size_t queue_capacity)
Configure a background job scheduler for the application.
cwist_error_t cwist_app_use_https3(cwist_app *app, bool enabled)
void cwist_app_ws(cwist_app *app, const char *path, cwist_ws_handler_func handler)
Register a WebSocket upgrade endpoint with default options.
int cwist_app_dispatch_memory(cwist_app *app, const char *req_buf, size_t req_len, char **res_buf, size_t *res_len)
Run the full router/middleware/handler pipeline on a raw HTTP/1.x request held in a memory buffer,...
bool cwist_https_upgrade_handler(cwist_https_connection *conn, cwist_http_request *req, cwist_http_response *res)
cwist_error_t cwist_app_use_https(cwist_app *app, const char *cert_path, const char *key_path)
Enable TLS for the application using the supplied certificate pair.
cwist_db * cwist_app_get_db(cwist_app *app)
Return the active shared database wrapper for the application.
void cwist_app_delete_named(cwist_app *app, const char *path, const char *name, cwist_handler_func handler)
void cwist_app_use_pqc_layer(cwist_app *app, bool enabled)
Enable Post-Quantum Cryptography (PQC) hybrid key exchange layer.
cwist_redis_pool_t * cwist_app_get_redis_pool(cwist_app *app)
Return the application's Redis connection pool.
void cwist_assets_destroy(void *registry)
void cwist_assets_respond(cwist_http_request *req, cwist_http_response *res, const cwist_asset_match *match)
Fill a response for a matched asset.
int cwist_assets_clone(void **dst, const void *src)
Deep-copy a registry (used when an app is cloned).
bool cwist_assets_match(cwist_app *app, const cwist_http_request *req, cwist_asset_match *out)
Match an HTTP request to a registered asset.
Private wiring between app.c and the asset registry (assets.c).
Deferred-response (async handler) API for CWIST.
void cwist_async_dispatch_ack(cwist_async *a)
Internal: on the dispatch thread, after all middleware has unwound. Call exactly once; do not touch t...
bool cwist_async_abort(cwist_async *a, cwist_http_status_t status)
Complete the exchange with an error status and close the connection.
struct cwist_async cwist_async
struct cwist_async * cwist_async_dispatch_take(cwist_http_response *res)
cwist_error_t cwist_async_server_loop(int server_fd, cwist_app *app)
Run the async (reactor-based) HTTP/HTTPS server loop for C1M scale.
Auto-Caching Layer for repetitive requests.
void cwist_bdr_set_limits(cwist_bdr_t *bdr, size_t max_bytes, time_t max_entry_age_sec, uint64_t revalidate_hits)
Adjusts guard-rail policies for the in-memory cache.
void cwist_bdr_put_fixed(cwist_bdr_t *bdr, const char *method, const char *path, const void *data, size_t len)
Immediately cache a fixed static response on request 1.
void cwist_bdr_unpin(bdr_blob_t *pin)
Release a pin acquired by cwist_bdr_get_pinned().
void cwist_bdr_put(cwist_bdr_t *bdr, const char *method, const char *path, const void *data, size_t len)
Store a response in the cache (lock-free learn path).
void cwist_bdr_destroy(cwist_bdr_t *bdr)
Destroys a BDR context and frees all cached blobs.
const void * cwist_bdr_get_pinned(cwist_bdr_t *bdr, const char *method, const char *path, size_t *out_len, bdr_blob_t **out_pin)
Find a cached response and pin it for the duration of the serve.
const void * cwist_bdr_get(cwist_bdr_t *bdr, const char *method, const char *path, size_t *out_len)
Try to find a cached response.
const void * cwist_bdr_get_pinned_cursor(cwist_bdr_t *bdr, const char *method, const char *path, size_t path_len, size_t *out_len, bdr_blob_t **out_pin, cwist_bdr_cursor_t *cursor)
cwist_bdr_get_pinned() with a per-connection fast path.
cwist_bdr_t * cwist_bdr_create(void)
Creates a BDR context.
Application configuration management (.env, environment variables).
cwist_config * cwist_config_create(void)
Create a new configuration object.
void cwist_config_destroy(cwist_config *cfg)
Destroy a configuration object.
cwist_error_t cwist_db_open(cwist_db **db, const char *path)
Open a SQLite database and wrap the resulting connection in a CWIST handle.
void cwist_db_close(cwist_db *db)
Close a CWIST database wrapper and its underlying SQLite connection.
#define CWIST_MIB(x)
Convert binary mebibytes to bytes.
Safe string type with helper methods.
cwist_sstring * cwist_sstring_create(void)
Create a new sstring instance.
cwist_error_t cwist_sstring_append(cwist_sstring *str, const char *data)
Append a C string.
cwist_error_t cwist_sstring_assign(cwist_sstring *str, const char *data)
Assign a C string.
void cwist_sstring_destroy(cwist_sstring *str)
Destroy an sstring instance.
Simple JSON string builder utility.
struct cwist_redis_pool cwist_redis_pool_t
Redis connection pool handle.
void cwist_redis_pool_destroy(cwist_redis_pool_t *pool)
Destroy the pool and close all connections.
cwist_redis_pool_t * cwist_redis_pool_create(const char *host, int port, size_t max_conns)
Create a Redis connection pool.
uint32_t cwist_endpoint_opt_t
Bitmask type controlling endpoint behavior.
#define CWIST_ENDPOINT_FILE
#define CWIST_ENDPOINT_DEFAULT
Default option for new endpoints.
#define CWIST_ENDPOINT_FIXED
@ CWIST_ERR_INT16
used when checking common errcodes in Unix/Linux
cwist_error_t make_error(cwist_errtype_t type)
Creates an error object initialized with the specified type.
gRPC over HTTP/2 helpers for CWIST.
int cwist_grpc_routes_clone(struct cwist_app *dst, const struct cwist_app *src)
void cwist_grpc_routes_destroy(struct cwist_app *app)
Free all gRPC routes and per-route resources on an app.
const cwist_http2_stream_hooks * cwist_grpc_http2_hooks(void)
Access the gRPC HTTP/2 stream hooks table.
Standardised readiness and liveness probing for CWIST.
void cwist_app_healthz(cwist_http_response *res)
Populate an HTTP response with the healthz JSON payload.
HTTP/2 Protocol Definitions for CWIST.
cwist_error_t cwist_http2_serve_connection_ex(cwist_https_connection *conn, void *user_ctx, cwist_http2_request_handler_func handler, const cwist_http2_stream_hooks *hooks)
Serve one HTTP/2 connection until it closes. Verifies the preface, sends our SETTINGS,...
HTTP/3 (QUIC-based) Protocol Definitions for CWIST.
void(* cwist_webtransport_handler_func)(cwist_http_request *req, cwist_http_response *res, void *stream)
Callback function type for handling WebTransport sessions over HTTP/3.
void cwist_http3_set_webtransport_handler(cwist_http3_context *ctx, cwist_webtransport_handler_func handler)
Register a WebTransport session handler on the given HTTP/3 context.
void cwist_http3_destroy_context(cwist_http3_context *ctx)
Destroy an HTTP/3 context.
cwist_error_t cwist_http3_init_context(cwist_http3_context **ctx, const char *cert_path, const char *key_path)
Callback function type for handling WebTransport sessions over HTTP/3.
cwist_error_t cwist_http3_reload_context(cwist_http3_context *ctx, const char *cert_path, const char *key_path)
Hot-reload the certificate chain and key of a live HTTP/3 context.
cwist_error_t cwist_http3_server_loop(int udp_fd, cwist_http3_context *ctx, cwist_http3_request_handler_func handler, void *user_ctx)
Run the HTTP/3 server event loop.
cwist_error_t cwist_http3_init_context_ephemeral(cwist_http3_context **ctx)
Initialize an HTTP/3 context with an ephemeral self-signed certificate.
void cwist_http_live_idle_leave(void)
bool cwist_reuseport_enabled(void)
bool cwist_http_live_idle_enter(void)
cwist_http_request * cwist_http_receive_request(int client_fd, char *read_buf, size_t buf_size, size_t *buf_len, cwist_http_parse_error_t *err_out)
Receive and parse an HTTP request from a socket.
#define CWIST_HTTP_READ_BUFFER_SIZE
void cwist_http_send_error_response(int fd, int status, const char *msg)
Send a minimal error response (Connection: close) for a failed request.
#define CWIST_HTTP_MAX_HEADER_SIZE
size_t cwist_http_serialize_headers(cwist_http_response *res, char *buf, size_t buf_size)
Serialize only the status line and headers into a caller buffer.
cwist_error_t cwist_http_server_loop(int server_fd, cwist_server_config *config, void(*handler)(int, void *), void *ctx)
Run the main HTTP accept loop using the configured concurrency strategy.
cwist_error_t cwist_http_send_response(int client_fd, cwist_http_response *res)
Send a response over a socket.
cwist_http_parse_error_t
Failure reason reported by the request receive APIs.
@ CWIST_HTTP_PARSE_BODY_TOO_LARGE
@ CWIST_HTTP_PARSE_HEADER_OVERFLOW
@ CWIST_HTTP_PARSE_MALFORMED
@ CWIST_HTTP_PARSE_TE_UNSUPPORTED
@ CWIST_HTTP_PARSE_EXPECT_FAILED
int cwist_make_socket_ipv4(struct sockaddr_in *sockv4, const char *address, uint16_t port, uint16_t backlog)
Create an IPv4 socket and bind/listen.
char * cwist_http_header_get(cwist_http_header_node *head, const char *key)
Find a header value by key.
cwist_error_t cwist_http_send_response_head(int client_fd, cwist_http_response *res)
Send only the status line and headers (HEAD replies).
@ CWIST_HTTP_INTERNAL_ERROR
@ CWIST_HTTP_NOT_IMPLEMENTED
void cwist_http_request_destroy(cwist_http_request *req)
Destroy an HTTP request object.
cwist_sstring * cwist_http_stringify_response(cwist_http_response *res)
Serialize a response to a string.
cwist_error_t cwist_http_header_add(cwist_http_header_node **head, const char *key, const char *value)
Add a header to the list.
void cwist_http_response_destroy(cwist_http_response *res)
Destroy an HTTP response object.
cwist_http_response * cwist_http_response_create(void)
Create a new HTTP response object.
cwist_http_request * cwist_https_receive_request(cwist_https_connection *conn)
Read from the TLS stream until a full HTTP request has been assembled.
cwist_error_t cwist_https_server_loop(int server_fd, cwist_https_context *ctx, void(*handler)(cwist_https_connection *conn, void *), void *user_ctx)
cwist_error_t cwist_https_send_response(cwist_https_connection *conn, cwist_http_response *res)
Serialize an HTTP response and send it over an active TLS connection.
void cwist_https_destroy_context(cwist_https_context *ctx)
Free an HTTPS context and release its OpenSSL resources.
Application-level structured logging.
cwist_logger * cwist_logger_create(const char *name)
Create a logger.
void cwist_logger_destroy(cwist_logger *log)
Destroy a logger.
Production-grade metrics engine for CWIST.
void cwist_metrics_serve_http(struct cwist_http_response *res)
Convenience helper: populate an HTTP response with /metrics body.
HTTP Protocol Definitions and Helpers.
cwist_async_send_status_t cwist_http_send_response_coalesced(int client_fd, cwist_http_response *res, cwist_http_async_conn_t *conn, bool keep_alive, bool head_only)
Coalescing variant of the async send: serialize small responses directly into the connection's output...
int cwist_http_async_conn_fill(cwist_http_async_conn_t *conn)
Drain the socket into the connection stash. Stops at EAGAIN, and also after a short read: poll is lev...
bool cwist_http_async_rearm(int client_fd, cwist_reactor_t *reactor, cwist_http_async_conn_t *conn)
Re-arm an async connection for its next event.
long get_cpu_cores(void)
Return the number of CPUs available to this process.
long cwist_http_inflight_count(void)
cwist_async_send_status_t
@ CWIST_ASYNC_SEND_DEFERRED
@ CWIST_ASYNC_SEND_KEEPALIVE
time_t cwist_http_parse_date(const char *str)
Parse an HTTP-date string into a time_t.
int cwist_http_response_stream_end(cwist_http_response *res)
Terminate a streaming response with the final zero-length chunk and flush.
void cwist_http_format_date(time_t t, char *buf, size_t len)
Format a time_t as an HTTP-date (RFC 7231).
#define CWIST_HTTP_COALESCE_MAX
int cwist_http_coalesce_flush_blocking(int client_fd, cwist_http_async_conn_t *conn)
Flush the coalesced-output stash, blocking in poll() until drained.
cwist_recv_status_t cwist_http_receive_request_nb(cwist_http_async_conn_t *conn, cwist_http_request **out, cwist_http_parse_error_t *err_out)
Try to assemble one complete request from the stash without any blocking IO. On CWIST_RECV_OK the con...
@ CWIST_HTTP_NOT_MODIFIED
@ CWIST_HTTP_PARTIAL_CONTENT
@ CWIST_HTTP_RANGE_NOT_SATISFIABLE
int cwist_http_coalesce_error_response(cwist_http_async_conn_t *conn, int status)
Build a minimal text error response into the coalesce stash.
void cwist_http_response_set_body_ptr_managed(cwist_http_response *res, const void *ptr, size_t len, cwist_http_body_cleanup_fn cleanup, void *ctx)
Attach a managed zero-copy body pointer and optional cleanup hook to a response.
cwist_coalesce_flush_status_t
@ CWIST_COALESCE_FLUSH_ERROR
@ CWIST_COALESCE_FLUSH_PARKED
cwist_coalesce_flush_status_t cwist_http_coalesce_flush(int client_fd, cwist_http_async_conn_t *conn, bool keep_alive)
Flush the coalesced-output stash with one speculative write.
cwist_http_request * cwist_http_parse_request_len(const char *buf, size_t len)
Parse an HTTP/1.x request held in a memory buffer (length-based; the buffer may contain a binary body...
cwist_http_response * cwist_http_response_create_in_arena(void *arena)
Create a response inside an existing (request) arena. The response is bump-allocated from arena and s...
int cwist_http_response_serialize(cwist_http_response *res, char **out, size_t *out_len)
Serialize a response into a freshly allocated HTTP/1.x buffer (status line + headers + body)....
int cwist_http_coalesce_append(cwist_http_async_conn_t *conn, const void *data, size_t len)
Append bytes to a connection's coalesced-output stash.
HTTPS/TLS wrapper interface.
@ CWIST_HTTPS_PROTOCOL_HTTP2
void https_pool_destroy(void)
Shut down the global HTTPS thread pool: signal workers to exit, join them, close any connections stil...
cwist_error_t cwist_https_reload_context(cwist_https_context *ctx, const char *cert_path, const char *key_path, cwist_app *app)
Hot-reload the certificate chain and key of a live HTTPS context.
int https_pool_init(void)
Create the worker threads, mutex, and condition variables of the global HTTPS thread pool....
uint64_t cwist_https_idle_timeout_ms(void)
Idle keep-alive budget for parked HTTP/1.1 TLS connections, in ms. CWIST_HTTPS_IDLE_TIMEOUT_MS overri...
cwist_error_t cwist_https_send_response_head(cwist_https_connection *conn, cwist_http_response *res)
cwist_error_t cwist_https_init_context_with_options(cwist_https_context **ctx, const char *cert_path, const char *key_path, const cwist_https_options *options, cwist_app *app)
bool cwist_https_conn_idle(cwist_https_connection *conn)
True when conn has no buffered TLS bytes and nothing readable.
bool cwist_https_connection_uses_http2(const cwist_https_connection *conn)
Gracefully close an HTTPS connection and free its buffers.
bool cwist_https_park(cwist_https_connection *conn, uint64_t idle_ms, cwist_https_park_class cls)
Hand an idle connection back to the HTTPS pool's park set.
void https_pool_submit(int client_fd, cwist_https_context *ctx, void(*handler)(cwist_https_connection *, void *), void *user_ctx)
Queue a freshly accepted client socket whose TLS handshake still needs to run in a pool worker.
const char * cwist_query_map_get(cwist_query_map *map, const char *key)
Get a value by key.
void cwist_query_map_set(cwist_query_map *map, const char *key, const char *value)
Set a key-value pair.
void cwist_query_map_clear(cwist_query_map *map)
Clear all entries.
cwist_query_map * cwist_query_map_create(void)
Create a new query map.
cwist_websocket * cwist_websocket_upgrade(cwist_http_request *req, int client_fd)
Upgrade a standard HTTP request to a WebSocket connection.
bool cwist_websocket_upgrade_response(cwist_http_request *req, cwist_http_response *res)
Validate the upgrade request and fill a 101 Switching Protocols response.
void cwist_websocket_destroy(cwist_websocket *ws)
Destroy the WebSocket context.
High-Performance Read-Optimal Persistent Store using SQLite.
#define CWIST_NUKE_ERR_LOW_MEMORY
sqlite3 * cwist_nuke_get_db(void)
Get the raw SQLite3 handle for the In-Memory DB.
void cwist_nuke_close(void)
Close databases safely. Performs a final synchronization before closing handles.
int cwist_nuke_init(const char *disk_path, int sync_interval_ms)
Initialize Nuke DB.
#define CWIST_NUKE_OK
Return codes for cwist_nuke_init.
cwist_db_pool_t * cwist_db_pool_create(const char *path, size_t max_conns)
Create a pool of SQLite connections.
void cwist_db_pool_destroy(cwist_db_pool_t *pool)
Destroy the pool and close all connections.
struct cwist_db_pool cwist_db_pool_t
Opaque database connection pool handle.
Read-only walk of an app's route table for in-tree consumers (the runtime OpenAPI generator in openap...
void cwist_app_foreach_route(cwist_app *app, cwist_route_visit_fn visit, void *ctx)
void(* cwist_route_visit_fn)(void *ctx, cwist_http_method_t method, const char *path)
void cwist_scheduler_destroy(cwist_scheduler_t *s)
Destroy the scheduler and stop all worker threads.
struct cwist_scheduler cwist_scheduler_t
Opaque background job scheduler handle.
cwist_scheduler_t * cwist_scheduler_create(size_t worker_count, size_t queue_capacity)
Create a scheduler with a pool of background workers.
Unified Graceful Shutdown support for CWIST.
int g_cwist_drain_timeout_sec
Drain timeout in seconds before process exits.
int g_cwist_listen_fd
Registered TCP listening socket (for signal-handler closure).
atomic_int g_cwist_running
Global running flag. Checked by all server event loops.
void cwist_shutdown_install_handlers(void)
Install SIGTERM and SIGINT handlers for graceful shutdown.
void cwist_shutdown_restore_handlers(void)
Put back the SIGTERM and SIGINT handlers that were in place before cwist_shutdown_install_handlers()....
int g_cwist_udp_fd
Registered UDP socket for HTTP/3 (for signal-handler closure).
Refcount-free cached response blob, reclaimed via libttak EBR.
Main Application Context.
bool nuke_enabled
True when NUKE DB integration is active.
bool use_https2
TLS HTTP/2 (h2)
void * redis_pool
Redis connection pool (when enabled).
size_t max_mem_space
Max memory space for static file pool (0 = auto-detected * 2)
void * scheduler
Background job scheduler (when enabled).
cwist_webtransport_handler_func wt_handler
WebTransport session handler.
cwist_error_handler_func error_handler
Global fallback error handler.
int session_max_age
Session cookie max-age in seconds.
cwist_https_context * ssl_ctx
SSL context when TLS is enabled.
struct cwist_fix_server_mem * mem_manager
Memory manager for static asset caching and hot-reloading.
char * session_secret
Session signing secret (HMAC-SHA256).
bool use_http2
Cleartext HTTP/2 (h2c)
cwist_error_handler_entry * error_handlers
Per-status-code error handlers.
bool use_http3
Ephemeral (Zero-config) HTTP/3.
cwist_bdr_t * bdr_ctx
Legacy standalone BDR storage; never replayed by HTTP dispatch.
cwist_route_table * router
Router definition.
cwist_db * db
Shared database handle.
char * session_name
Session cookie name.
cwist_https_request_handler_func https_request_handler
char * tls_groups
Explicit TLS groups override (NULL = automatic)
void * db_pool
Database connection pool (when enabled).
char * db_path
Database path (if set).
bool use_https3
TLS HTTP/3.
struct cwist_rdbms_runtime * rdbms
Mounted RDBMS runtime (auto-detected PostgreSQL/MySQL/MariaDB)
struct cwist_config * config
Application configuration.
bool pqc_layer_enabled
PQC Layer enabled flag.
struct cwist_http3_context * h3_ctx
HTTP/3 QUIC Context.
cwist_middleware_node * middlewares
Head of the middleware chain.
cwist_static_dir * static_dirs
Static directory mappings.
void * grpc_routes
Unary gRPC route registry.
struct cwist_logger * logger
Application logger.
void * assets
Content-hashed in-memory assets (see <cwist/sys/app/assets.h>).
int latency_threshold_ms
Latency threshold to trigger caching.
Wrapper for Database Operations. Currently uses SQLite3.
cwist_error_handler_func handler
struct cwist_error_handler_entry * next
cwist_http_status_t status_code
Integrated error structure used throughout the system.
Represents a file loaded into the fixed memory pool.
void * data
Pointer to memory-tracked file contents.
size_t size
Size of the file in bytes.
char etag[40]
Precomputed ETag for conditional requests.
ttak_mem_node_t * node
Tracking node for libttak lifecycle.
time_t last_mod
Last modification time.
char * fs_path
Full filesystem path.
char * path
Relative path (URL path)
char last_mod_hdr[30]
Precomputed HTTP-date (29 chars + NUL)
Fixed Server Memory Manager.
size_t files_capacity
Capacity of the files array.
ttak_mem_tree_t file_tree
Lifetime tracking tree for file buffers.
size_t current_used
Bytes accounted for by active files.
uint64_t retire_grace_ns
Delay before recycling replaced buffers.
cwist_file_t * files
Array of tracked files.
size_t total_capacity
Total capacity (defaults to sum of files * 2)
HTTP/3 server context encapsulating lsquic/BoringSSL settings.
cwist_webtransport_handler_func wt_handler
int noprogress_timeout_ms
void(* datagram_cb)(const void *data, size_t len, void *user_ctx)
cwist_reactor_t * reactor
cwist_sstring * path
e.g., "/users/1"
void * async_conn
C1M connection shell when on the reactor path (internal).
struct cwist_app * app
Owning app context (if any).
bool ws_async_handoff
WebSocket async path took fd ownership; C1M layer must detach.
void * arena
Per-request arena for bump-allocated structs (internal).
void * private_data
Internal framework use (protocol-specific context).
cwist_query_map * path_params
Parsed path parameters (e.g. :id)
void * https_conn
HTTPS connection wrapper when on TLS path (internal).
cwist_http_header_node * headers
cwist_endpoint_opt_t endpoint_opts
Behavior hints for the active endpoint.
cwist_db * db
Shared database handle from cwist_app.
cwist_http_method_t method
HTTP Response Object. Supports standard string body or Zero-Copy pointer body.
cwist_endpoint_opt_t endpoint_opts
Mirrors req->endpoint_opts.
cwist_sstring * status_text
e.g., "OK"
bool use_file_stream
Fast File Streaming.
const void * ptr_body
Pointer to external data (e.g., mmap region)
cwist_http_status_t status_code
cwist_http_header_node * headers
bool is_ptr_body
Zero-Copy Pointer Body.
int(* stream_sink)(void *ctx, const char *data, size_t len)
Live sink (streaming dispatch only).
bool stream_mode
cwist_http_response_stream_begin() ran.
size_t ptr_body_len
Length of external data.
bool stream_failed
A live sink rejected a chunk/head.
bool stream_ended
cwist_http_response_stream_end() ran.
Linked list node for middleware chain.
struct cwist_middleware_node * next
cwist_middleware_ctx_destroy_func destroy
cwist_middleware_func_ex func_ex
cwist_middleware_func func
Counted multiport descriptor created from a normal C array.
bool valid
False when construction failed, e.g. too many ports.
unsigned short ports[CWIST_MULTIPORT_MAX_PORTS]
Additional TCP ports.
size_t count
Number of valid entries in ports.
Mounted RDBMS runtime state.
bool use_forking
Process per request.
char * data
Access directly only when raw handling is necessary.
Core Application Structure and Lifecycle Management.
#define CWIST_MULTIPORT_MAX_PORTS
cwist_app * cwist_multiport_get_app(cwist_app **app_ref, unsigned short port)
Detach one additional multiport port into its own tunable application.
void(* cwist_handler_func)(cwist_http_request *req, cwist_http_response *res)
Function pointer type for HTTP route handlers.
void(* cwist_error_handler_func)(cwist_http_request *req, cwist_http_response *res, cwist_http_status_t status)
Function pointer type for error handlers.
void(* cwist_ws_handler_func)(cwist_websocket *ws)
Function pointer type for WebSocket handlers.
void(* cwist_middleware_func_ex)(cwist_http_request *req, cwist_http_response *res, cwist_handler_func next, void *user_ctx)
Extended middleware type that also receives a user context.
void(* cwist_handler_ctx_destroy_func)(void *user_ctx)
Releases a route's user context once the route no longer needs it.
void(* cwist_handler_ex_func)(void *user_ctx, cwist_http_request *req, cwist_http_response *res)
Route handler that also receives the context it was registered with.
int(* cwist_stream_write_fn)(void *ctx, const char *data, size_t len)
void(* cwist_middleware_ctx_destroy_func)(void *user_ctx)
Destructor for a middleware user context.
struct cwist_route_table cwist_route_table
void(* cwist_middleware_func)(cwist_http_request *req, cwist_http_response *res, cwist_handler_func next)
Middleware type that receives req/res pair and the next stage in the chain.
struct cwist_stream_req cwist_stream_req_t
cwist_multiport_t cwist_create_multiport_from_array(const unsigned short *ports, size_t count)
Create a counted multiport descriptor from an explicit pointer and length.
#define CWIST_STREAM_CHUNK
int cwist_app_multiport(cwist_app **app_ref, unsigned short public_port, cwist_multiport_t ports)
Start one app facade across a public port and counted backend port list.
struct cwist_static_dir cwist_static_dir
WASI target feature detection shared by all CWIST sources.
bool cwist_websocket_async_attach(int fd, cwist_reactor_t *reactor, cwist_ws_on_message_t on_message, void *user_data, const uint8_t *initial, size_t initial_len)
Attach an upgraded fd to the reactor as an async WebSocket connection (no on_close callback variant).
void(* cwist_ws_on_message_t)(cwist_websocket_async *ws, cwist_ws_frame *frame, void *user_data)
Callback invoked for each complete WebSocket message on the C1M path.