Repository navigation
Routing
Headers: <cwist/app.h> (app routes), <cwist/net/http/mux.h> (standalone
router), <cwist/sys/app/endpoint_opts.h> (endpoint flags)
typedef void (*cwist_handler_func)(cwist_http_request *req, cwist_http_response *res);
void cwist_app_get (cwist_app *app, const char *path, cwist_handler_func handler);
void cwist_app_post (cwist_app *app, const char *path, cwist_handler_func handler);
void cwist_app_put (cwist_app *app, const char *path, cwist_handler_func handler);
void cwist_app_delete(cwist_app *app, const char *path, cwist_handler_func handler);
void cwist_app_patch (cwist_app *app, const char *path, cwist_handler_func handler);Registering the same method and path again replaces the earlier handler.
A segment that starts with : captures one path segment into
req->path_params:
static void get_comment(cwist_http_request *req, cwist_http_response *res) {
const char *post = cwist_query_map_get(req->path_params, "post");
const char *id = cwist_query_map_get(req->path_params, "id");
...
}
cwist_app_get(app, "/posts/:post/comments/:id", get_comment);These follow from the implementation and the behavior measured with
cwist_app_dispatch_memory():
- Routes without parameters are stored in a hash table (O(1) lookup). Parameterized routes are tried in order after an exact miss.
- The query string is not part of matching:
/users/42?q=1matches/users/:id, and the query is inreq->query_params. - A trailing slash is significant:
/x/does not match/x. - A parameter matches exactly one segment:
/users/42/extradoes not match/users/:id. - The method must match. A request for a registered path with another method,
including
HEADfor aGETroute, gets404 Not Found(no405and no automaticHEAD). RegisterHEADhandling explicitly if you need it. Static files and assets do answerHEAD. - On a miss, the dispatcher tries content-hashed assets, then static
directories, then the error handler for
404(see Error Handling).
typedef void (*cwist_handler_ex_func)(void *user_ctx, cwist_http_request *req,
cwist_http_response *res);
typedef void (*cwist_handler_ctx_destroy_func)(void *user_ctx);
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);
/* also cwist_app_post_ex, _put_ex, _delete_ex, _patch_ex */The handler receives the pointer it was registered with, so per-route state needs no globals. Language bindings use these to register closures.
Ownership of user_ctx passes to the app on every call. When destroy is not
NULL it runs exactly once with user_ctx:
- when the app is destroyed;
- when the same method and path (without
:paramsegments) is registered again by any routing function, unless the new registration passes the same non-NULLuser_ctx; - immediately, when registration fails. The function then returns INT16
-1; success is INT160.
Multiport sub-apps share the context without owning it. Pass
destroy = NULL to keep ownership with the caller. The handler can run on
several threads at once, so shared context needs its own locking.
typedef struct { const char *greeting; } greeter;
static void greet(void *ctx, cwist_http_request *req, cwist_http_response *res) {
const greeter *g = ctx;
(void)req;
cwist_sstring_assign(res->body, g->greeting);
}
greeter *g = cwist_alloc(sizeof(*g)); /* <cwist/core/mem/alloc.h> */
g->greeting = "hello";
cwist_app_get_ex(app, "/greet", greet, g, cwist_free);typedef uint32_t cwist_endpoint_opt_t;
#define CWIST_DYNAMIC (1u << 0) /* default */
#define CWIST_ENDPOINT_FIXED (1u << 1) /* request-invariant hint */
#define CWIST_ENDPOINT_FILE (1u << 2) /* large-file zero-copy path */
void cwist_app_get_opt(cwist_app *app, const char *path, cwist_handler_func handler,
cwist_endpoint_opt_t opts);
/* also _post_opt, _put_opt, _delete_opt, _patch_opt, _ws_opt */-
CWIST_ENDPOINT_FILEletscwist_http_response_send_file()stream files larger thanCWIST_HTTP_MAX_BODY_SIZEwithsendfile(2)on Linux, macOS and FreeBSD. -
CWIST_ENDPOINT_FIXEDdoes not enable a response cache ondev; every request reaches the handler. The v3.9 release adds a separateCWIST_ENDPOINT_PUBLIC_FIXEDflag. See FIXED Endpoint Cache.
Flags combine with |. cwist_endpoint_has(opts, flag) tests a flag
(cwist_endpoint_has_extern() is the out-of-line version for bindings).
Inside a handler, req->endpoint_opts holds the active route's flags.
cwist_app_get_named(app, "/posts/:id", "post", show_post);
/* also _post_named, _put_named, _delete_named, _patch_named */
cwist_query_map *p = cwist_query_map_create();
cwist_query_map_set(p, "id", "7");
char *url = cwist_url_for(app, "post", p); /* free with cwist_free() */
cwist_query_map_destroy(p);cwist_url_for() returns NULL for an unknown name and copies the path
unchanged when it has no parameters or params is NULL. A :param with no
value in params is left as :param.
Known issue: when it substitutes parameters, the current implementation drops
the leading / (/posts/:id becomes posts/7, while /about stays
/about). Prepend the slash yourself until this is fixed.
void cwist_app_ws(cwist_app *app, const char *path, cwist_ws_handler_func handler);
void cwist_app_ws_async(cwist_app *app, const char *path,
cwist_ws_on_message_t on_message, void *user_data);See WebSocket.
cwist_app_static(app, "/static", "./public");
cwist_app_static_with_cache(app, "/assets", "./dist", "public, max-age=31536000, immutable");Header: <cwist/net/http/mux.h>
cwist_mux_router is a router you can use on its own, outside cwist_app
(for example inside a custom server loop). It supports exact routes (hashed),
:param routes, routes that end in *, route groups with a path prefix, and
per-route middleware.
cwist_mux_router *r = cwist_mux_router_create();
cwist_mux_handle(r, CWIST_HTTP_GET, "/health", health);
cwist_mux_handle(r, CWIST_HTTP_GET, "/files/*", files);
cwist_mux_group *api = cwist_mux_group_create(r, "/api/v1");
cwist_mux_group_handle(api, CWIST_HTTP_GET, "/users/:id", get_user); /* /api/v1/users/:id */
cwist_mux_route *route = cwist_mux_find_route(r, CWIST_HTTP_GET, "/health");
cwist_mux_route_use(route, my_middleware);
if (!cwist_mux_serve(r, req, res)) {
res->status_code = CWIST_HTTP_NOT_FOUND;
}
cwist_mux_group_destroy(api);
cwist_mux_router_destroy(r);| Function | Description |
|---|---|
cwist_mux_router *cwist_mux_router_create(void) |
New router. |
void cwist_mux_router_destroy(cwist_mux_router *router) |
Free the router and its routes. |
void cwist_mux_handle(cwist_mux_router *router, cwist_http_method_t method, const char *path, cwist_http_handler_func handler) |
Register a route. |
cwist_mux_route *cwist_mux_find_route(cwist_mux_router *router, cwist_http_method_t method, const char *path) |
Look a route up. |
cwist_mux_group *cwist_mux_group_create(cwist_mux_router *router, const char *prefix) |
Route group with a path prefix. |
void cwist_mux_group_handle(cwist_mux_group *group, cwist_http_method_t method, const char *path, cwist_http_handler_func handler) |
Register inside a group. |
void cwist_mux_group_destroy(cwist_mux_group *group) |
Free the group (routes stay in the router). |
void cwist_mux_route_use(cwist_mux_route *route, cwist_middleware_func mw) |
Attach middleware to one route. |
bool cwist_mux_serve(cwist_mux_router *router, cwist_http_request *req, cwist_http_response *res) |
Dispatch; false when no route matched. |
Design notes and references for the router's matching algorithm: Mux Algorithm References.
CWIST wiki, written against the dev branch of c4punks/CWIST. Pages marked "Source:" are generated from files under docs/; fix those in the repository. Questions: Discord.
Getting started
- Installation
- Quick Start
- Linking
- Server Modes
- Configuration and Environment
- Project CLI
- Tutorial / Korean
- Examples and Tutorials
Guides
Core
- API Reference
- Application
- Routing
- Middleware
- Requests and Responses
- Async Handlers
- Streaming Responses
- Error Handling
- Graceful Shutdown
- Multiport
Protocols
- HTTPS and TLS
- HTTP/2
- HTTP/3 and QUIC
- WebTransport
- WebSocket
- Server-Sent Events
- gRPC Server
- gRPC Client
- Protobuf and Codegen
- GraphQL
- WebRTC DataChannels
- HTTP Clients
Web features
- Static Files and Assets
- Big Dumb Reply Cache
- Compression
- Cookies
- Sessions and Flash
- Query Maps
- Multipart Uploads
- HTML Components
- Templates
- JSON
- Validation
- OpenAPI
Security
Data
Runtime
- Memory Management
- Full GC
- Async GC Ownership
- SString
- Reactor and I/O
- Metrics and Health
- Logging
- Testing
Platforms
Performance notes
- Benchmark Methodology
- C1M File Limits
- Reactor Fairness
- Cooperative Queuing
- Classic Pool Starvation
- Reactor Wakeup
- wrk Dual Histogram
- FIXED Endpoint Cache
- ADR-0001
- Durable Queue Gate
- Mux References
Project