Overview
libmicrohttpd wraps GNU libmicrohttpd — a small C library that lets you embed a full HTTP/1.1 server inside any program. No separate server process required; the listener runs in your PascalAI application alongside your agents and business logic.
It handles the full HTTP protocol: persistent connections, chunked transfer encoding, pipelining, TLS via GnuTLS, and POST body parsing for both form-urlencoded and multipart data. Three threading models cover everything from single-threaded embedded devices to high-concurrency API servers.
CLib package
Requires libmicrohttpd-dev on the host system. Ubuntu/Debian: sudo apt install libmicrohttpd-dev
Key Features
| Feature | Details |
| HTTP/1.1 | Persistent connections, chunked transfer encoding, request pipelining |
| TLS | HTTPS via GnuTLS; pass PEM cert and key file paths to StartTLS |
| Thread modes | Internal select loop, one thread per connection, or fixed thread pool |
| POST / forms | Incremental POST body processing; form-urlencoded and multipart file uploads |
| Auth | HTTP Basic authentication with realm challenge; Digest auth via underlying lib |
| Suspend / resume | Pause a connection mid-handler for async back-end calls, then resume it |
| Static files | Serve files directly from disk with NewFileResponse; uses sendfile(2) |
| IPv4 + IPv6 | Dual-stack support out of the box on all platforms |
Thread Modes
| Mode | Value | Use case |
| mmSelectInternally | 0 | Single background thread runs the select loop. Good for low-traffic services or when you want minimal resource use. |
| mmThreadPerConn | 1 | Spawns one thread per accepted connection. Simple handler code with no non-blocking constraints. Works well for long-lived connections. |
| mmThreadPool | 2 | Fixed pool of N worker threads shared across all connections. Best throughput for many short requests. Use StartPool(Port, N). |
Comparison
| Library | Notes |
| vs nginx | MHD is embeddable inside your process; nginx is a standalone reverse proxy/server. Choose MHD when you want one binary with no external dependencies. |
| vs Mongoose | Similar scope and philosophy. MHD is GNU-licensed with stronger portability guarantees and a more conservative, stable C API. |
| vs cpp-httplib | cpp-httplib is C++ only and header-only. MHD is pure C with a stable ABI — easier to bind from Pascal. |
Quick Start
Minimal REST API server (GET /hello, POST /echo)
uses libmicrohttpd;
var d := Start(8080, mmThreadPerConn);
WriteLn('Listening on http://localhost:8080');
var req: TMHDRequest;
var conn: TMHDConn;
while WaitRequest(d, 5000, req, conn) do
begin
if (req.Method = 'GET') and (req.Path = '/hello') then
SendJSON(conn, 200, '{"message":"Hello, world!"}')
else if (req.Method = 'POST') and (req.Path = '/echo') then
SendJSON(conn, 200, req.Body) { echo the raw body back }
else
NotFound(conn);
end;
Stop(d);
JSON API with Basic auth and thread pool
uses libmicrohttpd;
const
API_USER = 'admin';
API_PASS = 's3cr3t';
var d := StartPool(8443, 4); { 4-thread pool on port 8443 }
WriteLn('API server on :8443 (4 threads)');
var req: TMHDRequest;
var conn: TMHDConn;
while WaitRequest(d, -1, req, conn) do
begin
{ Guard all routes with Basic auth }
if not CheckBasicAuth(conn, API_USER, API_PASS) then
begin
ChallengeBasicAuth(conn, 'My API');
continue;
end;
if req.Path = '/api/status' then
begin
var active := ActiveConnections(d);
SendJSON(conn, 200,
'{"status":"ok","connections":' + IntToStr(active) + '}');
end
else if req.Path = '/api/files' then
begin
var resp := NewFileResponse('/var/www/index.html');
SetContentType(resp, 'text/html; charset=utf-8');
SendResponse(conn, 200, resp);
end
else
NotFound(conn);
end;
Stop(d);
Types
| Type | Underlying | Description |
| TMHDDaemon | Integer | Running HTTP server handle returned by Start / StartTLS / StartPool |
| TMHDConn | Integer | Per-request connection handle; passed to all response and auth functions |
| TMHDResp | Integer | Response object built by NewResponse, NewFileResponse, or NewStreamResponse |
| TMHDPostProc | Integer | POST body processor for form-urlencoded and multipart data |
| TMHDMode | enum | Threading model: mmSelectInternally, mmThreadPerConn, mmThreadPool |
| TMHDRequest | record | Populated by PollRequest / WaitRequest; contains Method, URL, Path, Query, Version, RemoteIP, Headers, Body, ContentLen |
| TMHDStatus | enum | Named HTTP status codes (200 msOK … 503 msServiceUnavailable) for use as integer literals |
Server Lifecycle
| Function | Description |
| Start(Port, Mode) | Start an HTTP server on the given port using the specified threading mode. Returns a daemon handle. |
| StartTLS(Port, Mode, CertPEM, KeyPEM) | Start with HTTPS. CertPEM and KeyPEM are paths to PEM files on disk. |
| StartPool(Port, Threads) | Convenience: start with mmThreadPool and a fixed number of worker threads. |
| Stop(D) | Gracefully shut down the server and free all associated resources. |
| RunLoop(D, TimeoutMs) | Drive the select loop manually from your own event loop (only needed with mmSelectInternally). |
Request Handling
| Function | Description |
| PollRequest(D, out Req, out Conn) | Non-blocking poll for the next request. Returns True if a request is ready; False if the queue is empty. |
| WaitRequest(D, TimeoutMs, out Req, out Conn) | Block until a request arrives or the timeout (ms) elapses. Pass -1 to wait indefinitely. |
| GetHeader(Conn, Name) | Return the value of a specific request header by name, or empty string if absent. |
| GetQueryParam(Conn, Name) | Return the value of a URL query parameter (e.g. ?id=42 → GetQueryParam(conn, 'id') = '42'). |
Response Building
Build a response object, optionally attach headers, then send it with SendResponse. The library destroys the response after sending.
| Function | Description |
| NewResponse(Body) | Create a response with a string body (held in memory). |
| NewFileResponse(Path) | Create a response backed by a file on disk. Uses sendfile(2) for zero-copy transfer. Supports range requests automatically. |
| NewStreamResponse | Create a chunked streaming response; write chunks to it incrementally. |
| AddHeader(Resp, Name, Value) | Attach an arbitrary HTTP response header. |
| SetContentType(Resp, ContentType) | Shortcut to set the Content-Type header. |
| SendResponse(Conn, Status, Resp) | Send the response with the given HTTP status code and destroy the response object. |
Convenience Helpers
One-liner helpers that create, configure, and send a response in a single call.
| Function | Description |
| SendJSON(Conn, Status, JSON) | Send a JSON string. Sets Content-Type: application/json automatically. |
| SendText(Conn, Status, Text) | Send plain text. Sets Content-Type: text/plain. |
| SendHTML(Conn, Status, HTML) | Send an HTML string. Sets Content-Type: text/html. |
| Redirect(Conn, URL, Permanent) | Send a 301 (permanent) or 302 (temporary) redirect to URL. |
| NotFound(Conn) | Send a 404 Not Found response with an empty body. |
POST Body
Use the POST processor API for form submissions and file uploads. Feed body data incrementally as it arrives, then extract named fields.
| Function | Description |
| NewPostProc(Conn) | Create a POST body processor for this connection. Handles both application/x-www-form-urlencoded and multipart/form-data. |
| PostProcFeed(PP, Data) | Feed a chunk of the raw POST body into the processor. Call repeatedly as data arrives. |
| GetPostField(PP, Name) | Retrieve the value of a named form field after all chunks have been fed. |
| FreePostProc(PP) | Release the POST processor and its internal buffers. |
uses libmicrohttpd;
{ Handle a form POST to /submit }
var req: TMHDRequest;
var conn: TMHDConn;
var d := Start(8080, mmSelectInternally);
while WaitRequest(d, -1, req, conn) do
begin
if (req.Method = 'POST') and (req.Path = '/submit') then
begin
var pp := NewPostProc(conn);
PostProcFeed(pp, req.Body);
var name := GetPostField(pp, 'name');
var email := GetPostField(pp, 'email');
FreePostProc(pp);
SendJSON(conn, 200, '{"ok":true,"name":"' + name + '"}');
end
else
NotFound(conn);
end;
Stop(d);
Authentication
| Function | Description |
| CheckBasicAuth(Conn, Username, Password) | Verify the HTTP Basic credentials in the request Authorization header. Returns True if they match. |
| ChallengeBasicAuth(Conn, Realm) | Send a 401 Unauthorized response with a WWW-Authenticate: Basic realm="..." challenge header. |
Info
| Function | Description |
| ActiveConnections(D) | Returns the current number of open connections on the daemon. |
| MHDVersion | Returns the version string of the underlying libmicrohttpd C library (e.g. '0.9.77'). |
Tip — thread safety
WaitRequest / PollRequest are the only daemon-level calls you make from your main loop. Once you have a TMHDConn handle, all response functions for that connection are safe to call from any thread — useful with mmThreadPool.