IOXD(7)libioxd Programmer's ManualIOXD(7)

NAME

ioxd - an HTTP/1.1 server library on io_uring, one worker per core, a stackful coroutine per connection

SYNOPSIS

#include <ioxd.h>

cc main.c $(pkg-config --cflags --libs ioxd) -o server

DESCRIPTION

libioxd serves HTTP/1.1, plain or over TLS 1.3, from a thread per core. Each worker owns an io_uring ring, a ring of receive buffers the kernel delivers into, and its own sockets on every bound port (SO_REUSEPORT); nothing is shared between workers while serving. Every connection runs on its own coroutine, so a handler reads the body and writes the reply in straight-line code: a call that has to wait for the wire suspends the coroutine, and the worker's loop resumes it on the completion.

A program registers its routes, sets the runtime's knobs if it wants to, binds its ports, then runs:

static void hello(ioxd_ctx *ctx)
{
    ioxd_slice name = ctx->req.route_params[0].value;
    ioxd_printf(ctx, "hello %.*s\n", (int)name.len, name.p);
}

int main(void)
{
    IOXD_GET("/hello/:name", hello);
    ioxd_bind(8080, NULL);                                  /* plain */
    ioxd_bind(8443, ioxd_certs_load("certs"));              /* TLS 1.3, from a directory of certificates */
    return ioxd_run(0);                                     /* one worker per core, until SIGINT or SIGTERM */
}

The request and the reply

A handler receives an ioxd_ctx: the request as plain data - method, path, query, headers, parameters, all slices into the connection's buffers - and the response being shaped. The body stays on the wire until asked for: ioxd_body_all reads it whole, ioxd_body_read_until streams it. The reply is written into a slab with ioxd_write, ioxd_printf or the JSON writer; when everything fits it goes out in one send with its head in front, and when it does not it streams, chunked. What goes on the wire follows the protocol whatever the handler did: a reply to HEAD carries no body, a declared length is held to, a request whose framing cannot be trusted is refused before a handler sees it.

Routes

Endpoints live in groups: a prefix plus middleware, nesting. Everything is resolved once, when the run starts, into a segment tree and one flat middleware chain per endpoint, so a request costs one walk and no scan. The script macros (IOXD_GROUP, IOXD_GET, IOXD_USE) are the same registrations written as a block.

Threads and lifetimes

Register routes, configure and bind from the main thread, before the run. Handlers run on worker threads, one at a time per worker; a request's slices are valid until the handler returns, and anything a handler hands the reply (a header, a content type) is copied. Nothing in the library is shared between workers except the read-only route tree and a TLS store's certificate table, which is reference counted.

Limits

A request head, and a body read whole, must fit the reader's 16 KB; streamed bodies have no limit. At most 64 request headers, 32 query parameters, 8 route captures, 16 added reply headers within 3 KB. These size the context, so ioxd_run refuses an application built with different values. The runtime's own sizes - the ring, the receive buffers, the coroutine stacks, the pools - are the configuration, per worker.

Building

Linux 6.x on x86-64, gcc 14 or newer (the library is C23; the headers are usable from C11), OpenSSL 3 for the TLS handshake (built by default; make TLS=0 or -DIOXD_TLS=OFF leaves it out). make produces libioxd.a and libioxd.so; make install the headers and a pkg-config file; CMake exports ioxd::ioxd. Kernel TLS needs a kernel with SOCKET_URING_OP_SETSOCKOPT (6.7 or newer) when the registered file table is on, which is the default.

FILES

<ioxd.h>
the whole API: an umbrella over the headers below
<ioxd/config.h>
the runtime's knobs: ring, buffers, stacks, pools
<ioxd/http.h>
request, response, context, body, reply, bind and run
<ioxd/router.h>
groups, endpoints, middleware, the script macros
<ioxd/slice.h>
slices, conversions, key/value parsing
<ioxd/json.h>
the JSON writer and IOXD_JSON_STRUCT
<ioxd/pipe.h>
a connection as a pipe, for protocols other than HTTP
<ioxd/tls.h>
a certificate store, for a TLS port

SEE ALSO

every public name, ioxd_examples(7), and the repository at github.com/MDA2AV/libioxd.

libioxd 0.1.02026-09-09IOXD(7)