Prerequisites. You can read C and know the basic purpose of an event loop or HTTP server. This guide defines the example workflow, ownership models, platform boundaries, and framework-specific terms it uses.
The 24 self-contained integrations intentionally demonstrate the same optional application workflow: combine one rate-limited resource consumption with one latency guard, run only granted work, and report that work’s measured service latency. Choose by ownership model and platform, then check the test-evidence table.
These examples show how to drive rl-c-client from common C event loops, HTTP
servers, and parsers. They use only the public client headers under include/.
Each source file starts with a numbered flow and an ownership summary; read
those comments before transplanting the integration into an application.
Resource requests and latency reports are independent core operations. The combined sequence below is deliberately shared by the examples so that their event-loop and ownership behavior can be compared directly; applications do not have to adopt it. See the repository README and API guide for the general model.
flowchart LR
Host["Host event loop or HTTP framework"]:::neutral --> Admission["Start resource request<br/>one consumption + one guard"]:::neutral
Admission --> Runtime["Public runtime<br/>nonblocking UDP + DNS"]:::neutral
Runtime --> Service["Ratelimitly service<br/>evaluates admission"]:::neutral
Service --> Callback["Completion callback"]:::neutral
Callback --> Decision{"Allowed?"}:::neutral
Decision -->|No| Reject["Reject without this work<br/>or a synthetic sample"]:::danger
Decision -->|Yes| Work["Run protected work"]:::success
Work --> Measure["Measure with monotonic clock"]:::success
Measure --> Report["Send one latency sample"]:::success
Report --> Runtime
classDef neutral fill:#EAECEF,stroke:#7D8590,color:#1A1A1A;
classDef danger fill:#FCE8E6,stroke:#B0413E,color:#1A1A1A;
classDef success fill:#E6F4EA,stroke:#1E7E45,color:#1A1A1A;
| Example | Integration model | Main technique |
|---|---|---|
| Latency tracker | Guard and reporting workflow | Gate work, measure it, then report one sample |
| libuv | Native event loop | uv_poll_t plus a one-shot uv_timer_t |
| libevent | Native event loop | Persistent EV_READ plus evtimer |
| GLib/GIO | Portable main loop | Non-owning GIOChannel watches plus timeout source |
| libev | Compact event loop | ev_io watchers plus a one-shot ev_timer |
| sd-event | systemd event loop | sd_event_add_io plus monotonic time source |
| kqueue | Native macOS/BSD readiness | Direct kevent wait plus request deadline |
| libdispatch | Serial dispatch queue | Read sources plus a one-shot timer source |
| Win32 | Native WinSock wait loop | WSAEVENT readiness plus deadline timeout |
| libhv | Native event loop | hio_t readiness plus htimer_t |
| liburing | Linux completion ring | IORING_OP_POLL_ADD through liburing |
| epoll | Linux readiness API | Direct epoll_wait with a request deadline |
| io_uring | Raw Linux completion ring | Syscalls and shared ring mappings |
| Mongoose | Single-thread HTTP loop | Pending connection state on mg_mgr_poll |
| CivetWeb | Worker-thread HTTP server | Queue plus a dedicated client thread |
| GNU libmicrohttpd | External HTTP loop | Suspended connections and merged select |
| H2O | Native HTTP event loop | Descriptor watchers and pool cleanup |
| Lwan | Coroutine HTTP server | Yielding handlers and a dedicated client thread |
| libreactor | Native HTTP event loop | Descriptor and timer objects on one thread |
| facil.io | Native HTTP event loop | Pause/resume handles with retained timers |
| Onion | Worker-thread HTTP server | OCS_YIELD long-poll lifecycle |
| Kore | Task-based HTTP server | Sleeping requests and task-channel results |
| Ulfius | Threaded HTTP server | A private client in each endpoint callback |
| llhttp | Parser only | Fragmented input and resumable backpressure |
“Yes” means this example’s source and build files directly support the platform, not merely that the upstream framework can run there. “Conditional” identifies a dependency or handle-width caveat explained in that folder’s README. A dash means the platform API is unavailable or this particular ownership pattern is scoped more narrowly; choose the linked native or portable alternative instead.
| Example | Linux | macOS | Windows | Scope note |
|---|---|---|---|---|
| Latency tracker | Yes | Yes | — | POSIX poll; use Win32 for the same workflow on Windows. |
| libuv | Yes | Yes | Yes | Uses uv_poll_init_socket to preserve WinSock handles. |
| libevent | Yes | Yes | Yes | Keeps sockets in evutil_socket_t. |
| GLib/GIO | Yes | Yes | Conditional | GLib exposes a Win32 socket constructor, but this source still needs a native handle-width proof and Windows execution test. |
| libev | Yes | Yes | — | This source targets libev’s Unix fd backends. |
| sd-event | Yes | — | — | libsystemd API. |
| kqueue | — | Yes | — | Also applies to BSD hosts. |
| libdispatch | Conditional | Yes | — | Open-source libdispatch is required on Linux. |
| Win32 | — | — | Yes | Native WSAEVENT; Wine/CrossOver test harness included. |
| libhv | Yes | Yes | Conditional | Verify that the libhv build preserves 64-bit SOCKET width. |
| liburing | Yes | — | — | Linux io_uring wrapper. |
| epoll | Yes | — | — | Linux kernel readiness API. |
| io_uring | Yes | — | — | Raw Linux UAPI; IORING_ENTER_EXT_ARG requires Linux 5.11 or newer. |
| Mongoose | Yes | Yes | Yes | Portable single-threaded server loop. |
| CivetWeb | Yes | Yes | — | This bridge uses pthreads, pipe, and poll. |
| GNU libmicrohttpd | Yes | Yes | — | This source uses the POSIX external-select model. |
| H2O | Yes | Yes | — | Selects epoll or kqueue. |
| Lwan | Yes | — | — | Linux coroutine server. |
| libreactor | Yes | — | — | Linux-focused descriptor API. |
| facil.io | Yes | Yes | — | facil.io 0.7 POSIX descriptor model. |
| Onion | Yes | Conditional | — | Onion v0.8 needs compatibility fixes for current macOS Clang. |
| Kore | Yes | Yes | — | ELF module on Linux; bundle on macOS. |
| Ulfius | Yes | Yes | — | This source deliberately uses POSIX poll. |
| llhttp | Yes | Yes | Yes | Parser adapter plus a portable select host driver. |
The examples intentionally cover several ownership models. Choose based on the concurrency model already used by the host application.
| Host architecture | Start with | Trade-off |
|---|---|---|
| Any host using latency load shedding | Latency tracker, then the matching loop or framework | Shows policy and measurement lifecycle separately from framework plumbing. |
| One event-loop thread | libuv, libevent, libhv, epoll, io_uring, Mongoose, H2O, libreactor, or facil.io | No client lock is needed when all calls stay on the loop thread. |
| Worker-thread HTTP server | CivetWeb, Lwan, or Onion | A dedicated bridge thread serializes client access; queue and shutdown ownership need care. |
| Isolated request tasks | Kore | Each task owns its client and reports through the framework’s task channel. |
| Lowest-complexity threaded handler | Ulfius | A private client is simple, but the handler waits and client setup repeats per request. |
| HTTP parser without a loop | llhttp | The host must provide TCP I/O, UDP readiness, deadlines, and connection lifetime. |
Prefer a single long-lived client per event loop or bridge thread in a busy service. The per-request Ulfius pattern is included because its ownership is especially easy to understand, not because it minimizes setup cost.
Every example uses the public r_client_runtime.h helper to keep transport
setup separate from its framework-specific control flow. The runtime:
r_client_on_datagram(); andDNS resolution is synchronous to keep the examples focused. Production event
loops should normally use the core client layer (r_client.h +
r_client_io.h) with their own asynchronous resolver instead of the runtime;
see “Choosing an integration layer” in docs/api.md.
r_runtime_client_on_readable() treats malformed and unauthenticated datagrams
as packet-local noise: it discards them and keeps draining. A non-OK return
therefore represents a real socket or client failure, not hostile junk traffic.
Set the authentication key before running an example:
export RATELIMITLY_AUTH_KEY=rl-aes1...
The runtime decodes the tenant key ID and discovers
_ratelimitly._udp.c-<key-id>.p0.ratelimitly.com. Set
RATELIMITLY_TENANT only to override production discovery for another DNS
zone:
export RATELIMITLY_TENANT=tenant.example.com
An override zone must publish SRV target hostnames whose first label encodes
each server’s ID as s-<decimal> (see the DNS section of
IO_ABSTRACTION.md); targets without that label are
silently ignored, and requests return RCLIENT_ERR_DNS if none remain.
For local development, bypass DNS and point the runtime at the repository’s synthetic responder:
export RATELIMITLY_EXAMPLE_SERVER_HOST=127.0.0.1
export RATELIMITLY_EXAMPLE_SERVER_PORT=39082
Leave the fixed endpoint variables unset in a normal deployment. The runtime
then discovers _ratelimitly._udp.<tenant> SRV records.
Build the library from the repository root, then enter one example folder:
make
cd examples/libuv
make
Each folder has its own README.md, Makefile, and CMakeLists.txt. Exact
framework compiler flags can vary by distribution; the build files use names
exported by upstream projects or common system packages and expose override
variables where a dependency is built from source.
Run all repository tests, including the example inventory and public runtime tests, from the repository root:
make test
CI also builds every Linux one-shot integration against its real event-loop or
parser dependency. tests/linux-one-shot-examples.txt
is the executable matrix. Each binary runs against the authenticated synthetic
responder in three deterministic scenarios:
The responder checks tracker TTL, sample limits, buffer size, threshold, and that the report uses the same latency-tracker ID as the preceding guard. Latency-tracker IDs and credentials never appear in test logs.
The HTTP integrations have the same behavioral coverage in three parallel CI
shards. tests/linux-http-examples.txt
records each executable, listener port, metrics label, and framework-specific
resource-denial status. For every server, CI makes real HTTP requests and
requires all of the following:
200, executes protected work, and emits exactly
one latency report for the guard’s tracker;429 (403 for Lwan) without work or a report;503 without work or a report; andEach server runs in its own process group. The harness drains UDP after the response and again during shutdown, so worker processes cannot hide duplicate or forbidden late reports from the responder assertions.
The example suite separates deterministic integration behavior from real P0
compatibility. Pull requests and forks receive only the synthetic responder;
trusted main pushes and owner-dispatched main runs add production checks.
flowchart LR
Change["Pull request or fork"] --> Synthetic["Per-example synthetic scenarios"]
Main["Trusted main"] --> Synthetic
Main --> Live["Per-example production P0 admission"]
Main --> Probe["Dedicated P0 semantic probe"]
Synthetic --> Contract["Allow, rate deny, latency deny, and paired report"]
Live --> Compatibility["Real discovery, authentication, work, and report send"]
Probe --> Readback["Exact latency read-back and positive-deficit rate outcome"]
| Layer | Scope | What it proves |
|---|---|---|
| Synthetic responder | 11 Linux one-shot, 10 Linux HTTP, and Win32 | Every example wires one resource and one latency guard, blocks denied work, and reports exactly once only after admission. |
| Per-example P0 | The same 22 CI-eligible integrations; Win32 under Wine and native MSVC | Key-derived production discovery, authenticated admission, protected work, and a locally successful fire-and-forget report path. |
| Dedicated P0 probe | Names unique to the CI run | Production reads back exactly 37 ms and returns a one-token rate-limited outcome with a positive deficit. |
| Local macOS | kqueue and libdispatch | The same deterministic contract on a developer Mac; no production credential. |
All trusted-main production calls use a conservative CI-only request profile: a 25 ms unit, three internal replays, one final receive unit, and a 125 ms maximum admission wait. Each runner logs the actual client wait and completion round/phase. This does not change the release/default policy of a 20 ms unit and one replay.
The per-example P0 lane does not claim server acknowledgement of every latency datagram: latency reporting is fire-and-forget. Server-observed semantics belong to the dedicated read-back probe. See the production P0 CI guide for trust boundaries, credential handling, concurrency, exact coverage, and failure interpretation.
kqueue and libdispatch are intentionally verified only on a developer Mac. They are absent from the CI workflow; run their strict builds and the same allow/resource-deny/latency-deny contract locally with:
bash tests/test_macos_examples.sh
tests/macos-local-examples.txt is the
small source of truth for that local-only matrix.
latency_tracker/main.c demonstrates both halves of
one application-level latency feedback loop:
r_latency_guard_t.latency_tracker_id from a stable tracker name and
every setting that defines its stored state.rl-c-client and expire when the callback returns.CLOCK_MONOTONIC.r_client_admission_report_latency(). The
admission object preserves the matching tracker ID and configuration
and prevents a second report for the same admitted request.In this combined workflow, never fabricate latency for work rejected by the guard. No operation occurred, so a zero or synthetic sample would corrupt the latency tracker. This rule does not prevent independently reporting other services or work. Likewise, do not measure the Ratelimitly request round trip unless that round trip is itself the service whose latency should control admission.
The guard’s policy fields have distinct purposes:
| Field | Meaning |
|---|---|
threshold_ms |
Reject protected work when tracked service latency reaches this value. |
ttl_ms |
Maximum age of samples retained for this tracker. |
max_samples |
Maximum sample count considered by the tracker. |
buffer_size |
Tracker storage request; must fit the credential quota. |
min_sample_threshold |
Samples required before the tracker estimate controls admission. |
The report repeats latency_tracker_id, ttl_ms, max_samples, buffer_size, and
min_sample_threshold so it updates the same tracker. threshold_ms belongs
only to the guard decision.
Build and run the standalone workflow:
cd latency_tracker
make
# Optional: simulated protected-work duration; default is 25 ms.
export RATELIMITLY_EXAMPLE_WORK_MS=25
./latency-tracker-example
Passing output resembles:
guard passed: resource and latency checks admitted the work
latency reported: service=example-inventory-backend observed=<elapsed> ms
<elapsed> is a monotonic measurement and can be greater than the configured
sleep because scheduler delay is part of the elapsed time.
The sleep represents a backend call only in this command-line demonstration.
Production event loops should start the real asynchronous operation after the
guard passes, record its monotonic start time, and report from its completion
callback. r_client_report_latency() is fire-and-forget: it uses the client’s
UDP send hook immediately and creates no request deadline or response watcher.
The examples in this section submit one combined admission check, print its decision, run and measure admitted work, report one latency sample, and exit. They are small references for readiness and deadline integration rather than HTTP servers.
Model. libuv/main.c registers both UDP sockets with
uv_poll_t. A one-shot
uv_timer_t is rearmed from the current request deadline after every event.
The loop thread owns the client and all watcher callbacks.
cd libuv
make
./libuv-example
Production note. Keep watcher teardown on the loop thread. Do not destroy the shared client while a poll or timer callback can still run.
Model. libevent/main.c uses persistent EV_READ events
for UDP ingress and an evtimer for the active request. The event-base thread
owns the client.
cd libevent
make
./libevent-example
Production note. Free every successfully created event, including partial initialization paths, before destroying the client.
Model. glib/main.c attaches non-owning GIOChannel watches for the
runtime’s UDP sockets and uses a one-shot GLib timeout for the request deadline.
The same source supports Unix sockets and WinSock handles.
cd glib
make
./glib-example
Production note. Remove every source and unref every non-owning channel before destroying the runtime that owns the underlying sockets.
Model. libev/main.c uses ev_io for UDP readiness and re-arms a one-shot
ev_timer from the current admission deadline.
cd libev
make
./libev-example
Production note. Stop all fd watchers before destroying runtime sockets. This example targets Unix fd backends rather than narrowing WinSock handles.
Model. sd_event/main.c combines sd_event_add_io socket sources with a
one-shot monotonic time source derived from the relative admission delay.
cd sd_event
make
./sd-event-example
Production note. Keep wall-clock client deadlines and monotonic timer timestamps in separate domains; convert through a relative delay.
Model. kqueue/main.c registers runtime UDP sockets with EVFILT_READ and
passes the current relative admission delay directly to kevent.
cd kqueue
make
./kqueue-example
Production note. Treat EV_ERROR and terminal EV_EOF as watcher errors,
and close the kqueue before destroying runtime-owned sockets.
Model. libdispatch/main.c serializes client calls on a private queue,
using read sources for UDP sockets and a timer source for admission deadlines.
cd libdispatch
make
./libdispatch-example
Production note. Cancel sources and serialize runtime teardown on the same queue. A semaphore gives this teaching program a finite shutdown path.
Model. win32/main.c associates each native SOCKET with a WSAEVENT and
passes the admission delay to WSAWaitForMultipleEvents.
cd win32
make CC=x86_64-w64-mingw32-gcc \
RL_CLIENT_LIBRARY=/path/to/windows/librclient.a \
OPENSSL_PREFIX=/path/to/mingw/openssl
Production note. Preserve pointer-width SOCKET values. Clear event
associations before closing events or destroying runtime-owned sockets.
Model. libhv/main.c attaches each client descriptor with
hio_add(..., HV_READ) and maps the request deadline to a one-shot htimer_t.
All client calls remain on the libhv loop thread.
cd libhv
make LIBHV_PREFIX=/path/to/libhv-install
./libhv-example
Production note. If application work can run on other threads, marshal it onto this loop instead of calling the shared client concurrently.
Model. liburing/main.c submits one
IORING_OP_POLL_ADD per UDP socket and
uses io_uring_wait_cqe_timeout() to cap each wait at the request deadline.
Each completion is consumed before the corresponding poll is rearmed.
cd liburing
make
./liburing-example
Production note. User-data values identify watcher completions. Reserve a non-overlapping namespace if the same ring also serves application I/O.
Model. epoll/main.c registers the nonblocking UDP
descriptors directly. It recomputes the timeout passed to epoll_wait() from
the client deadline on every iteration.
cd epoll
make
./epoll-example
Production note. Treat EPOLLERR and EPOLLHUP as terminal watcher
failures; repeatedly retrying a broken descriptor creates a busy loop.
Model. io_uring/main.c uses Linux UAPI headers and
syscalls directly. It maps the submission and completion rings, submits
IORING_OP_POLL_ADD, and supplies the deadline through IORING_ENTER_EXT_ARG.
cd io_uring
make
./io-uring-example
Production note. This is an educational raw-ring implementation. liburing is usually the safer compatibility layer for production code, especially when supporting several kernel versions or sharing a ring with other operations.
Unless a section says otherwise, these examples listen on port 8000. Send
GET /limited to run a check. Method filtering follows each framework, and
some teaching routes also accept other methods; production code should reject
unsupported methods before starting admission. An abandoned request is
cancelled where the framework exposes a reliable disconnect lifecycle.
Model. mongoose/main.c leaves an HTTP connection pending
while its check runs. mg_mgr_poll() drains client UDP sockets, advances request
deadlines, and cancels the check if the connection closes. One Mongoose thread
owns all state.
cd mongoose
make MONGOOSE_ROOT=/path/to/mongoose
./mongoose-example
curl -i http://127.0.0.1:8000/limited
Production note. The sample uses a 10 ms Mongoose poll interval. A host with stricter latency or power requirements should derive its wait from both Mongoose work and the nearest client deadline.
Model. civetweb/main.c keeps the client on one dedicated
poll thread. CivetWeb workers enqueue jobs and wait on per-request condition
variables, so no two threads enter the client concurrently. An admitted request
runs measured protected work on the bridge and reports it to the latency tracker.
Server workers stop before bridge state is destroyed.
cd civetweb
make CIVETWEB_ROOT=/path/to/civetweb
./civetweb-example
curl -i http://127.0.0.1:8000/limited
Production note. Waiting consumes one CivetWeb worker per in-flight check. Size the worker pool for the expected concurrency or adapt the bridge to an asynchronous response API if the service holds many simultaneous requests.
Model. libmicrohttpd/main.c runs MHD in
external-select mode. It suspends an HTTP connection during the asynchronous
combined admission check, measures and reports admitted protected work, resumes
the connection from the callback, and merges MHD’s timeout with the client
request deadline.
cd libmicrohttpd
make
./libmicrohttpd-example
curl -i http://127.0.0.1:8000/limited
Production note. Always call the MHD timeout API again after processing activity; both MHD and the client can move their nearest deadline.
Model. h2o/main.c wraps duplicate UDP descriptors with
H2O_SOCKET_FLAG_DONT_READ, so H2O reports readiness while the public runtime
consumes datagrams from the original sockets. Request-pool destructors cancel
abandoned checks, one-shot H2O timers enforce deadlines, and admitted protected
work is measured and reported to the latency tracker.
cd h2o
make
./h2o-example
curl -i http://127.0.0.1:8000/limited
Production note. The duplicate descriptors transfer readiness observation, not datagram ownership. Only the adapter should read the original client sockets, and its state must outlive every H2O watcher.
Model. lwan/main.c keeps client state off Lwan’s small
coroutine stacks. A dedicated thread owns the runtime and UDP sockets; handlers
enqueue combined admission checks and yield with lwan_request_sleep() until
completion. Reference-counted jobs remain valid after disconnect, and admitted
protected work is measured and reported on the bridge.
Build Lwan, then compile against its generated configuration and static library:
cd lwan
make LWAN_ROOT=/path/to/lwan LWAN_BUILD=/path/to/lwan/build
./lwan-example
curl -i http://127.0.0.1:8080/limited
The whole-archive flags retain Lwan’s linker-discovered module table. A shared
installation can instead use its generated lwan.pc. Lwan listens on port
8080 by default; change its normal configuration if that port is unsuitable.
Production note. Lwan’s public status table omits 429, so this example returns 403 for a denied check. Using an unknown numeric status triggers an assertion in supported Lwan versions.
Model. libreactor/main.c runs HTTP, UDP readiness,
and deadlines on one reactor thread. Duplicate descriptors observe readiness
while the public runtime owns datagram reads. Synchronous completions are
deferred so callback state is not freed while a client operation remains on the
stack. Allowed protected work is measured and reported before response.
cd libreactor
make
./libreactor-example
curl -i http://127.0.0.1:8000/limited
Production note. Preserve deferred completion if surrounding code can complete a request synchronously; immediate destruction would make reentrant callbacks unsafe.
Model. facil_io/main.c attaches duplicate UDP
descriptors as facil.io protocols and uses HTTP pause/resume for combined
admission checks. Timer callbacks retain request state through on_finish,
preventing a use-after-free when a UDP response races its deadline. One event
loop thread serializes client calls and reports admitted protected-work latency.
cd facil_io
make -C /path/to/facil.io lib
make FACIL_ROOT=/path/to/facil.io
./facil-io-example
curl -i http://127.0.0.1:8000/limited
Production note. Keep the explicit retain/release pairs around scheduled timers. A failed schedule must release the timer’s reference immediately.
Model. onion/main.c returns OCS_YIELD, so Onion workers
do not poll client sockets. A dedicated thread owns the runtime, runs combined
admission, and completes each yielded response through Onion’s long-poll
lifecycle. Shutdown cancels active checks and releases every yielded request
exactly once; admitted work is measured and reported before response.
cd onion
make ONION_ROOT=/path/to/onion-install
./onion-example
curl -i http://127.0.0.1:8000/limited
Production note. Keep response writes in the completion path documented by the Onion version being used. A bridge failure must wake all yielded requests with an error rather than leaving them suspended.
Model. kore/main.c runs each exchange as a kore_task,
putting the HTTP request to sleep until the task channel reports a combined
admission result. Task-local runtime ownership avoids cross-thread access and
reports admitted protected-work latency. The Linux module also declares the
extra socket syscalls required by Kore’s seccomp filter.
Build Kore with task support and its no-TLS backend, then build the module:
cd kore
make KORE_ROOT=/path/to/kore
kore -fnrc kore.conf
curl -i http://127.0.0.1:8000/limited
Production note. kore.conf grants the module’s plain-HTTP route and task
thread count. Reconcile the syscall declarations with a hardened deployment’s
own Kore build and sandbox policy.
Model. ulfius/main.c gives each endpoint callback a
private runtime and polls its UDP sockets on that callback’s libmicrohttpd
worker. This avoids shared mutable state, does not block the listener thread,
and measures/reports admitted protected work.
cd ulfius
make
./ulfius-example
curl -i http://127.0.0.1:8000/limited
Production note. One worker remains occupied and one client is initialized per request. For high concurrency, prefer a shared dedicated-thread bridge such as the CivetWeb or Onion pattern.
llhttp parses HTTP but does not provide an event loop. The self-contained
llhttp/ example collects URL fragments, removes query data from the
bucket key, and starts a combined resource-limit and latency-guard check when a
request completes. Its small select() driver makes the host contract runnable;
replace that driver with the TCP loop already used by your application.
Build the library and example after installing llhttp and its pkg-config file:
cd llhttp
make
./llhttp-example
The host lifecycle is:
rl_llhttp_adapter_init() once per accepted connection and check its
return status.rl_llhttp_adapter_feed().HPE_PAUSED, keep bytes at and after the returned consumed
offset. One check is already active.llhttp_resume(). Feed the
retained bytes only after that callback begins.rl_llhttp_adapter_finish() on clean TCP EOF, then
rl_llhttp_adapter_dispose() before releasing connection state.The pause is resumable backpressure for a pipelined next request; validation
failures use HPE_USER and are terminal parser errors. Allowed protected work
is timed monotonically and reported to the same tracker, while denied work is
never run or reported. The host owns TCP I/O, retained bytes, UDP readiness,
deadlines, and the adapter allocation. See
llhttp/README.md for complete ownership, build, platform,
and error-handling details.
| Term | Meaning |
|---|---|
| resource request | Core operation containing zero or more resource consumptions and zero or more latency guards. Non-empty requests go to r-servers; the empty request succeeds locally without network activity. |
| resource consumption | Quantity requested from one configured resource bucket. |
| latency report | Independent fire-and-forget operation containing one or more measured service latencies. |
| README | Folder-level guide that explains how to build, run, adapt, and test one integration. |
| GLib | Portable core library whose main loop drives one event-loop example. |
| GIO | GLib input/output APIs, including the GIOChannel socket wrapper used by that example. |
| CivetWeb | Embedded C HTTP server used by a dedicated bridge-thread example. |
| GNU | Free-software project under which libmicrohttpd is developed. |
| H2O | Event-driven C HTTP server whose embeddable loop drives one example. |
| POSIX | Portable Operating System Interface family used by the Unix-focused examples. |
| CrossOver | Commercial macOS compatibility environment that can run Windows executables in managed Wine bottles. |
| IPv4 | Internet Protocol version 4, one address family for the runtime’s UDP sockets. |
| IPv6 | Internet Protocol version 6, the other address family for the runtime’s UDP sockets. |
| admission | Combined resource and latency decision made before protected work starts. |
| protected work | Application operation—such as a database call or response build—that runs only after admission. |
| rate bucket | Stable resource identity whose quota is consumed by matching requests. |
| latency guard | Admission check that can shed new work when recent tracked latency reaches a threshold. |
| latency tracker | Server-side window of reported service durations used by a latency guard. |
| fire-and-forget | Send with no acknowledgement callback; local success proves the datagram was handed to UDP, not that the server accepted it. |
| P0 | Production Ratelimitly DNS tier derived from an API key unless a tenant DNS override is supplied. |
| synthetic responder | Repository test server that returns deterministic allow, resource-deny, and latency-deny decisions without a production credential. |
| one-shot example | Command-line integration that performs one admission workflow and exits, rather than serving HTTP continuously. |
| SRV | DNS service record that returns target hostnames and ports. |
| AAAA | DNS address record that maps a hostname to an IPv6 address. |
| WinSock | Native Windows sockets API, whose socket handle is pointer-width on 64-bit Windows. |
| MSVC | Microsoft Visual C/C++ compiler and native Windows toolchain. |
| UAPI | Linux user-space API exposed by kernel headers, used directly by the raw io_uring example. |
| ELF | Executable and Linkable Format used for Linux binaries and shared modules. |
| EOF | End of file or stream; for a socket, the peer has ended its byte stream. |
| BSD | Family of Unix-like operating systems that provides the kqueue API. |
| GET | HTTP method used by the examples’ read-only /limited route. |
| MHD | Prefix used by GNU libmicrohttpd’s public C API. |
| IOChannel | GLib GIOChannel wrapper that presents a socket or file descriptor as an event source. |
| backpressure | Pausing parsing or new work until an outstanding admission decision completes. |
io_uring_enter(2),
GLib GIOChannel, and
Microsoft SOCKET
support the external platform claims called out in this guide.