|
KEEL 1.0.0
Minimal C11 HTTP client/server library built on epoll/kqueue/io_uring/poll
|
#include <keel/allocator.h>#include <keel/request.h>#include <keel/response.h>#include <keel/body_reader.h>#include <stddef.h>

Go to the source code of this file.
Data Structures | |
| struct | KlRoute |
| struct | KlMiddlewareEntry |
| struct | KlRouter |
Typedefs | |
| typedef void(* | KlHandler) (KlRequest *req, KlResponse *res, void *user_data) |
| Route handler function. | |
| typedef int(* | KlMiddleware) (KlRequest *req, KlResponse *res, void *user_data) |
| Middleware function signature. | |
| typedef struct KlWsServerConfig | KlWsServerConfig |
| typedef struct KlRouter | KlRouter |
Functions | |
| int | kl_router_init (KlRouter *r, KlAllocator *alloc) |
| Initialize a router with an empty route table. | |
| int | kl_router_add (KlRouter *r, const char *method, const char *pattern, KlHandler handler, void *user_data, KlBodyReaderFactory body_reader) |
| Register a route. Pattern supports :param segments (e.g. "/users/:id"). | |
| int | kl_router_add_streaming (KlRouter *r, const char *method, const char *pattern, KlHandler handler, void *user_data, KlBodyReaderFactory body_reader) |
| Register a streaming-handler route. Identical to kl_router_add except the handler runs after the body reader is set up but BEFORE the body is fully received. The handler is expected to consume the body incrementally (e.g. via the streaming multipart pull iterator) and may yield mid-stream. The body reader's on_data callback is responsible for resuming the yielded handler when more bytes arrive. | |
| int | kl_router_add_streaming_async (KlRouter *r, const char *method, const char *pattern, KlHandler handler, void *user_data, KlBodyReaderFactory body_reader) |
| Register an async-streaming-handler route (v2.2.0+). | |
| int | kl_router_match (KlRouter *r, const char *method, size_t method_len, const char *path, size_t path_len, KlRoute **matched, KlParam *params, int *num_params) |
| Match a request against registered routes. HEAD requests automatically fall back to GET routes. | |
| int | kl_router_use (KlRouter *r, const char *method, const char *pattern, KlMiddleware fn, void *user_data) |
| Register pre-body middleware that runs before body reading. | |
| int | kl_router_use_post (KlRouter *r, const char *method, const char *pattern, KlMiddleware fn, void *user_data) |
| Register post-body middleware that runs after body reading. | |
| int | kl_router_run_middleware (KlRouter *r, KlRequest *req, KlResponse *res) |
| Run all matching pre-body middleware in registration order. | |
| int | kl_router_run_post_middleware (KlRouter *r, KlRequest *req, KlResponse *res) |
| Run all matching post-body middleware in registration order. | |
| int | kl_router_dispatch_synthetic (KlRouter *r, KlRequest *req, KlResponse *res, int run_middleware) |
| Run a fully-formed synthetic request through the router pipeline: match → pre-body middleware → post-body middleware → handler. | |
| void | kl_router_free (KlRouter *r) |
| Free router resources. | |
| typedef void(* KlHandler) (KlRequest *req, KlResponse *res, void *user_data) |
Route handler function.
| typedef int(* KlMiddleware) (KlRequest *req, KlResponse *res, void *user_data) |
Middleware function signature.
| typedef struct KlWsServerConfig KlWsServerConfig |
| int kl_router_init | ( | KlRouter * | r, |
| KlAllocator * | alloc | ||
| ) |
Initialize a router with an empty route table.
| r | Router to initialize. |
| alloc | Allocator for route table growth. |
| int kl_router_add | ( | KlRouter * | r, |
| const char * | method, | ||
| const char * | pattern, | ||
| KlHandler | handler, | ||
| void * | user_data, | ||
| KlBodyReaderFactory | body_reader | ||
| ) |
Register a route. Pattern supports :param segments (e.g. "/users/:id").
| r | Router instance. |
| method | HTTP method ("GET", "POST", "*" for any). |
| pattern | URL pattern to match. |
| handler | Handler function invoked on match. |
| user_data | Passed to handler and body reader factory. |
| body_reader | Factory for body reader, or NULL to discard body. |
| int kl_router_add_streaming | ( | KlRouter * | r, |
| const char * | method, | ||
| const char * | pattern, | ||
| KlHandler | handler, | ||
| void * | user_data, | ||
| KlBodyReaderFactory | body_reader | ||
| ) |
Register a streaming-handler route. Identical to kl_router_add except the handler runs after the body reader is set up but BEFORE the body is fully received. The handler is expected to consume the body incrementally (e.g. via the streaming multipart pull iterator) and may yield mid-stream. The body reader's on_data callback is responsible for resuming the yielded handler when more bytes arrive.
Post-body middleware is NOT run for streaming routes (the handler has already executed by the time the body completes).
A non-NULL body_reader factory is required.
| int kl_router_add_streaming_async | ( | KlRouter * | r, |
| const char * | method, | ||
| const char * | pattern, | ||
| KlHandler | handler, | ||
| void * | user_data, | ||
| KlBodyReaderFactory | body_reader | ||
| ) |
Register an async-streaming-handler route (v2.2.0+).
Identical to kl_router_add_streaming, plus: the handler is invoked BEFORE any leftover body bytes are fed via on_data. It MUST yield on NEED_DATA — the body reader's on_data callback will resume it when bytes arrive (both the leftover from the headers-read and subsequent socket reads). Enables the full error-path mid-stream early-exit: if the body reader rejects bytes (on_data returns -1) at any point, the parked handler is resumed via on_error and can catch the parser error to write a structured response. Routes that opt out (use the legacy kl_router_add_streaming) still get the partial early-exit covering caps that fire after dispatch but lose the structured response for caps that fire during leftover processing. Synchronous C handlers that consume the body without yielding (i.e. they call into the body reader expecting events to be immediately available) must use the legacy kl_router_add_ streaming — they'll see NEED_DATA on the first call here and have no way to recover.
| r | Router instance. |
| method | HTTP method. |
| pattern | URL pattern. |
| handler | Yield-on-NEED_DATA handler. |
| user_data | Opaque pointer. |
| body_reader | Body reader factory (NULL rejected). |
| int kl_router_match | ( | KlRouter * | r, |
| const char * | method, | ||
| size_t | method_len, | ||
| const char * | path, | ||
| size_t | path_len, | ||
| KlRoute ** | matched, | ||
| KlParam * | params, | ||
| int * | num_params | ||
| ) |
Match a request against registered routes. HEAD requests automatically fall back to GET routes.
| r | Router instance. |
| method | Request method string. |
| method_len | Length of method string. |
| path | Request path string. |
| path_len | Length of path string. |
| matched | Receives the matched route, or NULL. |
| params | Receives extracted :param values. |
| num_params | Receives the number of extracted params. |
| int kl_router_use | ( | KlRouter * | r, |
| const char * | method, | ||
| const char * | pattern, | ||
| KlMiddleware | fn, | ||
| void * | user_data | ||
| ) |
Register pre-body middleware that runs before body reading.
| r | Router instance. |
| method | HTTP method filter ("GET", "POST", "*" for any). |
| pattern | URL pattern — exact match or prefix with trailing slash-star. |
| fn | Middleware function. Return 0 to continue, non-zero to short-circuit. |
| user_data | Passed to fn on each invocation. |
| int kl_router_use_post | ( | KlRouter * | r, |
| const char * | method, | ||
| const char * | pattern, | ||
| KlMiddleware | fn, | ||
| void * | user_data | ||
| ) |
Register post-body middleware that runs after body reading.
Post-body middleware can access req->body_reader data. Short-circuiting preserves keep_alive since the body has already been consumed.
| r | Router instance. |
| method | HTTP method filter ("GET", "POST", "*" for any). |
| pattern | URL pattern — exact match or prefix with trailing slash-star. |
| fn | Middleware function. Return 0 to continue, non-zero to short-circuit. |
| user_data | Passed to fn on each invocation. |
| int kl_router_run_middleware | ( | KlRouter * | r, |
| KlRequest * | req, | ||
| KlResponse * | res | ||
| ) |
Run all matching pre-body middleware in registration order.
| int kl_router_run_post_middleware | ( | KlRouter * | r, |
| KlRequest * | req, | ||
| KlResponse * | res | ||
| ) |
Run all matching post-body middleware in registration order.
| int kl_router_dispatch_synthetic | ( | KlRouter * | r, |
| KlRequest * | req, | ||
| KlResponse * | res, | ||
| int | run_middleware | ||
| ) |
Run a fully-formed synthetic request through the router pipeline: match → pre-body middleware → post-body middleware → handler.
The caller is responsible for initialising req (method, path, headers, optional body_reader) and res (via kl_response_init). On return, res holds the response state the handler (or a short-circuiting middleware) produced; the caller is responsible for kl_response_free and for inspecting res->status, res->body, res->hdr_buf, etc.
req->num_params / req->params are filled in from the match before the pipeline runs; callers do not need to pre-populate them.
If run_middleware is 0, pre- and post-body middleware are skipped (only the matched handler runs). When the match fails (404/405) and middleware is disabled, no handler is invoked and the caller should read the return value to know what happened.
This is the in-process counterpart to the network-driven dispatch in connection.c / h2.c. Hull's test harness uses it; user code can use it for synthetic requests (e.g. agent-API self-calls).
| r | Router instance. |
| req | Pre-built request (must outlive the call). |
| res | Pre-initialised response (the call writes into it). |
| run_middleware | Non-zero to run pre- and post-body middleware. |
| void kl_router_free | ( | KlRouter * | r | ) |
Free router resources.