Header: <cwist/sys/app/app.h> (Proposed)
High-level abstractions for building web applications quickly.
cwist_app_create
Main Application Context.
cwist_app * cwist_app_create(void)
Creates a new CWIST application instance.
Initializes a new web application instance with default security settings.
cwist_app_use_https
Integrated error structure used throughout the system.
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.
Enables HTTPS for the application.
cwist_app_use_https2 / cwist_use_https2
cwist_error_t cwist_app_use_https2(cwist_app *app, bool enabled)
#define cwist_use_https2(enabled)
Keeps the default HTTPS request path in legacy HTTP/1.1 mode unless explicitly enabled. When enabled, CWIST rebuilds the TLS context with an HTTP/2-compatible TLS profile, negotiates h2 through ALPN, and swaps in the HTTPS/2 request-handler slot for the app. The HTTP/2 handler feeds decoded requests into the existing routing/middleware stack, so handler code does not need to change. The current implementation is intentionally narrow: one request stream is handled at a time per TLS connection, with support for the standard client preface, SETTINGS, HEADERS, CONTINUATION, DATA, PING, and GOAWAY.
cwist_app_use_pqc_layer
void cwist_app_use_pqc_layer(cwist_app *app, bool enabled)
Enable Post-Quantum Cryptography (PQC) hybrid key exchange layer.
Enables the post-quantum cryptography (PQC) hybrid TLS layer. When enabled is true, CWIST forces X25519MLKEM768:X25519:P-256 as the key-exchange group list, sets TLS 1.3 as the minimum version, and disables all legacy TLSv1.0–1.2 cipher suites. This provides transport-layer quantum resistance via the NIST-standard ML-KEM-768 (Kyber) hybridized with classical X25519 ECDH, without requiring any OpenSSL knowledge from the application. Application code never touches BoringSSL directly.
cwist_app_use_db
Wrapper for Database Operations. Currently uses SQLite3.
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.
cwist_db * cwist_app_get_db(cwist_app *app)
Return the active shared database wrapper for the application.
Opens (or reopens) a SQLite database and keeps the handle on the cwist_app. Each incoming request automatically receives the pointer via req->db, so handlers can run queries without wiring globals:
}
}
cwist_error_t cwist_db_query(cwist_db *db, const char *sql, cJSON **result)
Execute a SQL query and collect every row into a cJSON array.
cwist_error_t cwist_sstring_assign(cwist_sstring *str, const char *data)
Assign a C string.
__prim_cwist_error_t error
cwist_db * db
Shared database handle from cwist_app.
HTTP Response Object. Supports standard string body or Zero-Copy pointer body.
Routing
cwist_app_get / cwist_app_post
Registers standard HTTP handlers. Supports path parameters using :name syntax.
}
const char * cwist_query_map_get(cwist_query_map *map, const char *key)
Get a value by key.
cwist_query_map * path_params
Parsed path parameters (e.g. :id)
void cwist_app_get(cwist_app *app, const char *path, cwist_handler_func handler)
Registers a GET route handler.
cwist_app_get_ex / _post_ex / _put_ex / _delete_ex / _patch_ex
Register a route whose handler also receives a context pointer, so state can be bound to a route without globals (for example a closure from a language binding). Matching, :param paths, middleware and every transport behave exactly as for cwist_app_get().
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.
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.
Ownership of user_ctx passes to the app on every call. If destroy is not NULL it runs exactly once with user_ctx:
- when the app is destroyed;
- when the same method and path (one without
:param segments) is registered again, by any routing function, unless the new registration passes the same non-NULL user_ctx;
- immediately, when the registration fails. The function then returns INT16
-1; success is INT16 0.
Multiport sub-apps share the context without owning it. Pass destroy = NULL to keep ownership with the caller. The handler can run on several worker threads at once, so a shared context needs its own synchronization.
}
g->greeting = "hello";
void cwist_free(void *ptr)
Free memory obtained via cwist_alloc/cwist_strdup/etc.
void * cwist_alloc(size_t size)
Allocate zeroed memory tracked by libttak.
cwist_app_dispatch_memory
Runs the full router/middleware/handler pipeline on a raw HTTP/1.x request held in a memory buffer — no socket, thread, or event loop involved. The response is serialized (status line + headers + body) into a freshly allocated buffer the caller frees with cwist_free(). One-shot Connection: close semantics; this is the in-memory entry point for embedded transports such as WASM hosts.
const char *req = "GET /users/7 HTTP/1.1\r\nHost: x\r\n\r\n";
}
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,...
cwist_app_ws
Registers a WebSocket handler.
void(* cwist_ws_handler_func)(cwist_websocket *ws)
Function pointer type for WebSocket handlers.
void cwist_app_ws(cwist_app *app, const char *path, cwist_ws_handler_func handler)
Register a WebSocket upgrade endpoint with default options.
Routes without parameters are stored in a hash table for O(1) lookups while parameterized patterns fall back to sequential matching.
cwist_endpoint_opt_t
Each route can be annotated with a bitmask of behavioral flags:
| Flag | Description |
CWIST_DYNAMIC | Default dynamic handler behavior. |
CWIST_ENDPOINT_FIXED | Hint that the route's response is request-invariant. Does not activate caching in the current revision — every request is dispatched normally. See docs/fixed-cache-status.md. |
CWIST_ENDPOINT_FILE | Hint that the endpoint streams files, enabling Linux/BSD sendfile fast paths. |
Use the _opt helpers to set these flags:
"/download/:id",
#define CWIST_ENDPOINT_FILE
#define CWIST_ENDPOINT_FIXED
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.
Flags may be OR-ed together to combine behaviors (e.g., fixed + file).
Static Assets
cwist_app_static
cwist_error_t cwist_app_static(cwist_app *app, const char *url_prefix, const char *directory)
Serves a directory of static files at a URL prefix. Files are loaded into the fixed memory pool for Z...
Mounts a directory (path normalization + traversal guards) at a URL prefix. Static responses still pass through the middleware chain and use cwist_http_response_send_file for MIME detection, traversal protection, and HEAD-aware Content-Length.
Error Handling
cwist_app_set_error_handler
Registers a global error handler for status codes >= 400 (e.g., 404 Not Found).
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_app_set_error_handler(cwist_app *app, cwist_error_handler_func handler)
Install a custom HTTP error callback.
Unused Variables
Use the CWIST_UNUSED() macro to suppress compiler warnings for unused handler parameters.
}
#define CWIST_UNUSED(x)
Mark an intentionally unused variable to silence compiler warnings.
Concurrency & Thread-Safety
- Multithreading:
cwist_app_listen starts a multithreaded server (one thread per request). Handlers and middleware MUST be thread-safe.
- Global State: Avoid using global variables in your handlers. If you need shared state, ensure it is protected by appropriate synchronization primitives (e.g.,
pthread_mutex_t).
- Context Passing: Each handler receives
req->app/req->db for shared infrastructure, while req->private_data remains available for middleware-to-middleware communication.
Memory Ownership Rules
- Framework-Owned:
cwist_http_request and cwist_http_response objects passed to handlers are owned by the framework. Do NOT destroy them inside the handler.
- Borrowed Strings: Strings returned by
cwist_http_header_get or cwist_json_get_raw are borrowed. If you need to keep them after the builder or request is destroyed, you must copy them.
- Explicit Destruction: Objects created with
_create (e.g., cwist_json_builder_create, cwist_websocket_upgrade) MUST be destroyed by the caller using the corresponding _destroy function.
cwist_app_listen
int cwist_app_listen(cwist_app *app, int port)
Serve app on port until SIGTERM, SIGINT or cwist_shutdown_request(); blocks the calling thread.
Starts the server loop on the specified port.
In-Memory Test Client
Header: <cwist/sys/app/test_client.h>
cwist_test_client dispatches synthetic HTTP requests directly through the app's router and middleware without opening a socket, so integration tests run in-process with no ports and no teardown race.
cwist_test_client_create / cwist_test_client_destroy
void cwist_test_client_destroy(cwist_test_client *client)
Destroy a test client.
cwist_test_client * cwist_test_client_create(cwist_app *app)
Create a test client for an app.
Creates and destroys a test client bound to app. The client keeps a per-instance cookie jar that is carried automatically on subsequent requests.
Request helpers
const char *body, const char *content_type);
const char *body, const char *content_type);
const char *body, const char *content_type);
cwist_http_response * cwist_test_client_post_json(cwist_test_client *client, const char *path, const char *json_body)
Perform a POST request with JSON content-type.
cwist_http_response * cwist_test_client_put(cwist_test_client *client, const char *path, const char *body)
Perform a PUT request.
cwist_http_response * cwist_test_client_get(cwist_test_client *client, const char *path)
Perform a GET request.
cwist_http_response * cwist_test_client_delete(cwist_test_client *client, const char *path)
Perform a DELETE request.
cwist_http_response * cwist_test_client_patch(cwist_test_client *client, const char *path, const char *body)
Perform a PATCH request.
cwist_http_response * cwist_test_client_post(cwist_test_client *client, const char *path, const char *body)
Perform a POST request.
Each returns a cwist_http_response * owned by the caller; free with cwist_http_response_destroy().
For full control over headers, cookies, and query string use:
Optional parameters for advanced test requests.
cwist_http_response * cwist_test_client_request_ex(cwist_test_client *client, cwist_http_method_t method, const char *path, const cwist_test_client_request_options *opts)
Perform a request with full control over headers, cookies and query.
Cookie jar
const char *name, const char *value, const char *path);
void cwist_test_client_clear_cookies(cwist_test_client *client)
Clear all cookies in the jar.
void cwist_test_client_set_cookie(cwist_test_client *client, const char *name, const char *value, const char *path)
Set a persistent cookie in the client's cookie jar.
const char * cwist_test_client_get_cookie(cwist_test_client *client, const char *name)
Get a cookie value from the jar.
Test assertion macros
#define CWIST_ASSERT_BODY_CONTAINS(res, snippet)
#define CWIST_ASSERT_HEADER(res, header_name, expected_value)
#define CWIST_ASSERT_STATUS(res, expected_status)
Each macro prints a [ASSERT FAIL] message with file and line number and calls exit(1) on failure, matching the style of the framework's own test suite.