Skip to content

Application

Lee Yunjin edited this page Oct 7, 2026 · 1 revision

Application

Header: <cwist/app.h> (wraps <cwist/sys/app/app.h>)

A cwist_app owns everything a server needs: the route table, the middleware chain, protocol settings (TLS, HTTP/2, HTTP/3, WebTransport), attached services (database, pools, scheduler, gRPC routes, assets) and caches. A program usually creates one app, configures it, calls cwist_app_listen(), and destroys it after the server stops.

#include <cwist/app.h>

int main(void) {
    cwist_app *app = cwist_app_create();
    if (!app) return 1;

    cwist_app_use_db(app, "app.db");
    cwist_app_enable_metrics(app);
    cwist_app_enable_healthz(app);
    cwist_app_get(app, "/", index_handler);

    int rc = cwist_app_listen(app, 8080);   /* blocks */
    cwist_app_destroy(app);
    return rc == 0 ? 0 : 1;
}

Lifecycle

Function Description
cwist_app *cwist_app_create(void) Allocate an app with default settings. Returns NULL on allocation failure.
void cwist_app_destroy(cwist_app *app) Free the app and everything it owns: routes (running route-context destructors), middleware contexts, pools, the scheduler, caches.
int cwist_app_listen(cwist_app *app, int port) Serve until SIGTERM, SIGINT or cwist_shutdown_request(). Same as cwist_app_listen_ex(app, port, 0, -1).
int cwist_app_listen_ex(cwist_app *app, int port, int workers_override, int c1m_override) workers_override > 0 sets the worker process count (1 = no fork); c1m_override is 1 reactor, 0 classic pool, negative reads CWIST_C1M_MODE. Returns 0 after a graceful shutdown, -1 if the server could not start.
void cwist_apply_profile(void) Apply CWIST_PROFILE defaults to the environment; called by cwist_app_listen().
void cwist_app_set_max_memspace(cwist_app *app, size_t size) Capacity of the static-file memory pool (default: twice the size of the files found). Use CWIST_MIB(64) and friends from <cwist/core/macros.h>.

cwist_app_listen() forks worker processes (one per core unless CWIST_WORKERS says otherwise), raises the open-file limit, binds TCP (and UDP for HTTP/3) with SO_REUSEPORT, installs SIGTERM/SIGINT handlers for the duration of the call, and drains connections on shutdown. With more than one worker it returns in each worker process. See Server Modes and Graceful Shutdown.

Routing

Routes are covered in Routing. Summary:

cwist_app_get(app, "/users/:id", get_user);                  /* plain handler */
cwist_app_post_ex(app, "/items", create_item, ctx, cwist_free); /* handler with context */
cwist_app_get_opt(app, "/download/:id", download, CWIST_ENDPOINT_FILE);
cwist_app_get_named(app, "/posts/:id", "post", show_post);  /* for cwist_url_for() */
cwist_app_ws(app, "/chat", chat_handler);                    /* blocking WebSocket */
cwist_app_ws_async(app, "/live", on_message, NULL);          /* reactor WebSocket */
cwist_app_static(app, "/static", "./public");

Middleware

cwist_app_use(app, cwist_mw_access_log(CWIST_LOG_JSON));
cwist_app_use_ex(app, NULL, my_mw_ex, ctx, ctx_destroy);

See Middleware.

Protocols and TLS

Function Description
cwist_error_t cwist_app_use_https(cwist_app *app, const char *cert_path, const char *key_path) Serve TLS with the given PEM certificate and key. TLS 1.3+ only.
void cwist_app_use_pqc_layer(cwist_app *app, bool enabled) Force the hybrid post-quantum group list X25519MLKEM768:X25519:P-256 and TLS 1.3 minimum.
void cwist_app_set_tls_groups(cwist_app *app, const char *groups) Explicit colon-separated TLS group list; NULL restores automatic selection.
cwist_error_t cwist_app_use_https2(cwist_app *app, bool enabled) Negotiate HTTP/2 (h2) over TLS through ALPN.
cwist_error_t cwist_app_use_http2(cwist_app *app, bool enabled) Accept cleartext HTTP/2 (h2c, prior knowledge) on the plain TCP port.
cwist_error_t cwist_app_use_https3(cwist_app *app, bool enabled) Serve HTTP/3 over QUIC with the HTTPS certificate, on UDP at the same port number.
cwist_error_t cwist_app_use_http3(cwist_app *app, bool enabled) Serve HTTP/3 with an ephemeral self-signed certificate (no TLS setup; for development and tests).
void cwist_app_use_webtransport(cwist_app *app, cwist_webtransport_handler_func handler) Accept WebTransport sessions; also needs HTTP/3 enabled.
cwist_error_t cwist_app_use_ech(cwist_app *app, const char *ech_key, const char *ech_dir) Declared in <cwist/security/tls/ech.h>. With the bundled BoringSSL this is currently a no-op that returns success; the key and directory arguments are not used yet.

Convenience macros cwist_use_https2(enabled), cwist_use_https3(enabled), cwist_use_http2(enabled) and cwist_use_http3(enabled) call the functions above on a variable named app.

Details: HTTPS and TLS, HTTP/2, HTTP/3 and QUIC, WebTransport.

Attached services

Function Description
cwist_error_t cwist_app_use_db(cwist_app *app, const char *db_path) Open a SQLite database and expose it to every request as req->db.
cwist_error_t cwist_app_use_nuke_db(cwist_app *app, const char *db_path, int sync_interval_ms) Use Nuke DB (in-memory reads, synced to disk) as the app database.
cwist_db *cwist_app_get_db(cwist_app *app) The app database handle.
cwist_error_t cwist_app_use_db_pool(cwist_app *app, const char *db_path, size_t max_conns) Attach a SQLite connection pool.
cwist_db_pool_t *cwist_app_get_db_pool(cwist_app *app) The pool, or NULL.
cwist_error_t cwist_app_use_redis(cwist_app *app, const char *host, int port, size_t max_conns) Attach a Redis connection pool.
cwist_redis_pool_t *cwist_app_get_redis_pool(cwist_app *app) The Redis pool, or NULL.
cwist_error_t cwist_app_use_scheduler(cwist_app *app, size_t worker_count, size_t queue_capacity) Attach a background job scheduler.
cwist_scheduler_t *cwist_app_get_scheduler(cwist_app *app) The scheduler, or NULL.
bool cwist_app_auto_rdbms(cwist_app *app, int port) Probe 127.0.0.1:port for PostgreSQL, MySQL or MariaDB by wire protocol and record the result in app->rdbms.
void cwist_app_enable_metrics(cwist_app *app) Add GET /metrics (Prometheus text format).
void cwist_app_enable_healthz(cwist_app *app) Add GET /healthz, /live and /ready.
void cwist_app_enable_swagger(cwist_app *app, const char *mount_path, const char *openapi_json_path) Serve Swagger UI at mount_path for the given openapi.json.
int cwist_app_use_session(cwist_app *app, const char *secret) Enable signed cookie sessions; see Sessions and Flash.

Pages: Database, Redis, Scheduler, Metrics and Health, OpenAPI.

RDBMS auto-detection

cwist_rdbms_probe_port(port) connects to 127.0.0.1:port. It sends a PostgreSQL StartupMessage, or reads a MySQL handshake packet and tells MySQL from MariaDB by its version string. cwist_app_auto_rdbms() stores the detected provider and port in app->rdbms (cwist_rdbms_runtime, with ready = true). It does not open a client connection: CWIST has no PostgreSQL or MySQL driver. Use the result to choose the ORM SQL dialect or to configure your own driver.

Error handling

Function Description
void cwist_app_set_error_handler(cwist_app *app, cwist_error_handler_func handler) Fallback handler.
void cwist_app_register_error_handler(cwist_app *app, cwist_http_status_t status, cwist_error_handler_func handler) Handler for one status code; replaces an earlier one for the same code.
typedef void (*cwist_error_handler_func)(cwist_http_request *req,
                                         cwist_http_response *res,
                                         cwist_http_status_t status);

The dispatcher calls these when no route, asset or static file matches the request: the handler registered for 404, or else the fallback handler, runs with status = CWIST_HTTP_NOT_FOUND. Without either, CWIST answers 404 Not Found with a plain-text body. Other statuses set by handlers are sent as the handler wrote them. See Error Handling.

Caching

cwist_app_configure_bdr(app, max_bytes, max_entry_age_sec, revalidate_hits) sets the guard rails of the app's Big Dumb Reply cache. See Big Dumb Reply Cache and, for CWIST_ENDPOINT_FIXED, FIXED Endpoint Cache.

Running a request without a socket

Function Description
void cwist_app_dispatch(cwist_app *app, cwist_http_request *req, cwist_http_response *res) Run an already-parsed request through middleware and routing. Used by the test client.
int cwist_app_dispatch_memory(cwist_app *app, const char *req_buf, size_t req_len, char **res_buf, size_t *res_len) Parse a raw HTTP/1.x request from memory, dispatch it, and serialize the response into a new buffer (free with cwist_free() from <cwist/core/mem/alloc.h>). Connection: close semantics. Returns -1 for a malformed request or a body that cannot be serialized (file streams).
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 dispatch_memory but delivers the response through a chunk callback.
char *out; size_t out_len;
const char *raw = "GET /users/7 HTTP/1.1\r\nHost: x\r\n\r\n";
if (cwist_app_dispatch_memory(app, raw, strlen(raw), &out, &out_len) == 0) {
    fwrite(out, 1, out_len, stdout);
    cwist_free(out);
}

These entry points serve embedded hosts (WASM, Service Workers) and tests. The incremental-request and streaming variants are on Streaming Responses.

Multiport

cwist_app_multiport() serves one app on a public port plus a list of extra ports, each of which can be detached into its own tunable sub-app. See Multiport.

Threading and ownership rules

  • Handlers and middleware run on several threads at once (and, with more than one worker, in several processes). Shared state needs its own synchronization; per-process state is not shared between workers.
  • The framework owns the cwist_http_request and cwist_http_response passed to a handler. Do not destroy them.
  • Strings returned by getters such as cwist_http_header_get() and cwist_query_map_get() are borrowed from the request; copy them to keep them after the handler returns.
  • Objects you create with a _create function are yours to _destroy.
  • req->app and req->db give handlers the app and its database without globals; route contexts (_ex routes) bind per-route state.

The cwist_app structure

cwist_app is a public struct (include/cwist/sys/app/app.h). Read its fields only when no accessor exists (for example app->rdbms); configure the app through the functions above.

Clone this wiki locally