|
KEEL 1.0.0
Minimal C11 HTTP client/server library built on epoll/kqueue/io_uring/poll
|
A fact-based comparison of three embedded C HTTP libraries. Data gathered March 2026.
| Keel | Mongoose | libmicrohttpd | |
|---|---|---|---|
| First release | 2025 | 2004 | 2007 |
| License | MIT | GPLv2 / Commercial | LGPLv2.1+ |
| Core LOC | ~14K | ~33K (amalgamated) | ~19K |
| Architecture | 31 independent modules | Monolithic amalgam | Monolithic |
| Primary target | Embedded servers, edge services | Bare-metal MCU, IoT | Desktop/server embedding |
| GitHub stars | Early stage | 12,600+ | 135 (mirror; canonical repo on GNU Savannah) |
| Dimension | Keel | Mongoose | libmicrohttpd |
|---|---|---|---|
| License | MIT — no restrictions | GPLv2 or paid commercial license | LGPL — linking constraints |
| HTTP/2 | Server + client | Not supported | Not supported |
| Event backends | epoll, kqueue, io_uring, poll | select/poll only | select, poll, epoll |
| Modularity | 31 independent, testable modules | Single amalgamated file | Monolithic library |
| Allocator | Runtime vtable (bring-your-own) | Compile-time macros only | None (raw malloc) |
| TLS model | Pluggable vtable — any backend | Built-in TLS 1.3 + pluggable | GnuTLS only |
| HTTP parser | Pluggable vtable — swappable | Hardcoded | Hardcoded |
| Router + middleware | Built-in with :param capture, two-phase middleware | None — DIY if/else chains | None — single callback |
| CORS | Built-in configurable middleware | DIY | DIY |
| SSE | Dedicated zero-alloc API | DIY over chunked | DIY over chunked |
| HTTP client | Sync + async + streaming + H2 | Basic client | Server only — no client |
| Connection pooling | Client-side pool with keep-alive reuse | None | None |
| Redirect following | Built-in (RFC 7231 method transform) | None | None |
| Compression | Pluggable vtable (gzip via miniz, extensible) | None | None |
| Decompression | Pluggable client-side response decompression | None | None |
| Backpressure | Built-in write buffer (KlDrain) | None | None |
| Timers | Built-in min-heap scheduling | No dedicated API | No dedicated API |
| Cosmopolitan C | Supported (APE binaries) | Not supported | Not supported |
| Test density | 671 tests (40 suites) for ~14K LOC | ~4K LOC tests for 33K LOC | Fewer relative to size |
| Code size | ~14K LOC — auditable in a day | ~33K LOC (includes TCP/IP stack, drivers) | ~19K LOC |
| Dimension | Keel | Mongoose | libmicrohttpd |
|---|---|---|---|
| Maturity | New (2025–2026) | 20+ years, 12.6K stars | GNU project, 18+ years |
| Production deployments | Early stage | NASA ISS, Siemens, Samsung, Bosch — hundreds of millions of devices | NASA, Sony, Kodi, systemd |
| Security audits | Self-audited (fuzz, ASan, static analysis) | OSS-Fuzz continuous fuzzing | Formal audits by Least Authority (Mozilla) and Ada Logics |
| Bare-metal / MCU | Supported via lwIP/picoTCP (BSD socket compat) | Built-in TCP/IP stack for STM32, ESP32 etc. | Requires OS networking |
| Threading modes | Single-threaded (+ thread pool offload) | Single-threaded | 4 modes: external, internal, pool, thread-per-connection |
| MQTT | Not supported | Built-in | Not supported |
| Built-in auth | None (use middleware) | None | Digest + Basic auth built-in |
| Community size | Small | Large, corporate-backed | GNU ecosystem, distro-packaged everywhere |
| Platform breadth | Linux, macOS, Cosmopolitan | Linux, macOS, Windows, RTOS, bare-metal, 20+ MCU families | Linux, macOS, Windows, FreeBSD, z/OS, vxWorks |
| Drop-in integration | Makefile + headers | 2 files (mongoose.c + .h) — simplest possible | Autotools + pkg-config |
| JSON parsing | Not included | Built-in | Not included |
| Dimension | Notes |
|---|---|
| HTTP/1.1 compliance | All three are fully compliant |
| WebSocket | Keel and Mongoose: full support. libmicrohttpd: experimental |
| Keep-alive | All three support persistent connections |
| Chunked encoding | All three handle chunked transfer encoding |
| Multipart | All three parse multipart/form-data |
| Async suspend/resume | Keel: KlAsyncOp. libmicrohttpd: MHD_suspend/resume. Mongoose: no direct equivalent |
| Sanitizer testing | All three test under ASan/UBSan |
| Fuzz testing | All three have fuzz targets |
| sendfile | Keel: Linux + macOS. Mongoose: no. libmicrohttpd: Linux only |
| Streaming responses | All three support callback/chunked streaming |
Keel — per-route handlers with pattern matching:
Mongoose — single event handler, DIY dispatch:
libmicrohttpd — single callback, called multiple times per request:
Keel:
Mongoose:
libmicrohttpd:
Keel — runtime vtable:
Mongoose — compile-time macros:
libmicrohttpd — no custom allocator interface. Uses malloc/free directly.
Choose Keel when:
Choose Mongoose when:
Choose libmicrohttpd when:
Mongoose embeds its own TCP/IP stack (~8K LOC). Keel takes a different approach: bring your own.
lwIP, picoTCP, and CycloneTCP all provide BSD-compatible socket APIs (accept, read, write, close, poll, getaddrinfo). Keel's existing code links against these symbols unchanged — no transport vtable, no abstraction layer, no added complexity.
What you need:
BACKEND=poll (lwIP provides poll() via its socket compat layer)-DKL_NO_SIGNAL (bare-metal has no POSIX signals)thread_pool.c from CORE_SRC if no RTOS/pthreads (thread pool is optional)What already works without changes:
sendfile() — already has a pread + write fallback for non-Linux/macOS platformsSO_REUSEPORT — already guarded with #ifdefMSG_NOSIGNAL / SO_NOSIGPIPE — already guarded with #ifdefsysconf(_SC_NPROCESSORS_ONLN) — already has KL_TP_DEFAULT_WORKERS overrideMain loop pattern on bare-metal:
This is the same pattern Mongoose uses (mg_mgr_poll in a while(1) loop). The difference: Mongoose bundles the TCP/IP stack, Keel lets you choose.
Why no transport vtable? Every major embedded TCP/IP stack provides POSIX-compatible socket functions. A vtable abstracting read() over read() adds indirection without value. If a future stack with a fundamentally different API appears, the vtable can be added then — Keel doesn't abstract until there's a real need.