A small HTTP/1.1 server library written in C11, with no dependencies beyond a
POSIX system. One poll() loop, one thread, non-blocking sockets.
#include "chttp.h"
static int hello(chttp_request *request, chttp_response *response, void *user_data)
{
(void)request;
(void)user_data;
return chttp_response_send_text(response, 200, "hello world\n");
}
int main(void)
{
chttp_config config = chttp_config_default();
chttp_server *server = chttp_server_new(&config);
chttp_server_route(server, CHTTP_GET, "/", hello, NULL);
chttp_server_run(server);
chttp_server_free(server);
return 0;
}- HTTP/1.1 and HTTP/1.0 with correct persistent-connection defaults,
Connection: closehandling and a configurable keep-alive request cap. - Incremental parser that never needs a whole request in memory at once and accepts data one byte at a time.
- Chunked transfer decoding, including chunk extensions and trailer fields.
Expect: 100-continueanswered before the body is read.- Request pipelining: buffered requests are handled in order.
- Router with
:namecaptures, a trailing*wildcard, automaticHEADhandling and405replies carrying anAllowheader. - Query strings and percent-decoding for paths and arguments.
- Static files with MIME lookup,
ETag,Last-Modified,If-None-Match, byte ranges (206/416) and traversal protection. - Streaming responses from disk in fixed slices instead of loading files into memory.
- Limits everywhere: request line, header block, body size, connection count and idle timeout are all bounded and configurable.
make # static library in build/libchttp.a
make examples # build/bin/hello and build/bin/static_server
make test # unit tests plus socket level integration tests
make sanitize # rebuild and run the tests under ASan and UBSan
make format # clang-format over src, include, examples and tests
sudo make install # PREFIX=/usr/local by defaultRequires a C11 compiler (GCC or Clang) and a POSIX platform. Tested on Linux and macOS.
make examples
./build/bin/hello 8080 &
curl -i localhost:8080/hello/notion
curl -i -X POST --data 'ping' localhost:8080/echo
curl -i localhost:8080/nope
./build/bin/static_server 8081 public &
curl -i localhost:8081/
curl -i -H 'Range: bytes=0-31' localhost:8081/index.htmlLink against build/libchttp.a and add include/ to your include path:
cc -std=c11 -Iinclude my_server.c build/libchttp.a -o my_serverA handler receives the parsed request and an empty response, fills the
response in, and returns CHTTP_OK:
static int show_user(chttp_request *request, chttp_response *response, void *user_data)
{
const char *id = chttp_request_param(request, "id"); /* /users/:id */
const char *fields = chttp_request_query(request, "fields");
(void)user_data;
chttp_response_status(response, 200);
chttp_response_header(response, "Content-Type", "application/json");
return chttp_response_print(response, "{\"id\":\"%s\",\"fields\":\"%s\"}", id,
fields != NULL ? fields : "all");
}
chttp_server_route(server, CHTTP_GET, "/users/:id", show_user, NULL);
chttp_server_static(server, "/assets", "public");Date, Server, Content-Length and Connection are written by the server,
so a handler cannot produce a mis-framed response. chttp_server_stop() is
safe to call from a signal handler.
docs/api.md— every public function, type and config field.docs/architecture.md— the event loop, the parser state machine, the module layout and the deliberate limitations.
include/chttp.h public API, the only header a user needs
src/buffer.* growable byte buffer
src/kv.* name/value lists for headers, query args, captures
src/util.* strings, dates, monotonic clock, token lists
src/log.* levelled logging to stderr
src/http.c method and status tables
src/url.* percent-decoding and query parsing
src/mime.* extension to content type table
src/request.* incremental request parser
src/response.* response builder and header serialisation
src/router.* pattern matching
src/file.* static file handler
src/connection.* per-connection state machine
src/server.c sockets, poll loop, lifecycle
examples/ runnable servers
tests/ unit and integration tests
This is a readable, self-contained server, not a replacement for nginx. There
is no TLS, no HTTP/2, no sendfile, no request routing across threads and no
response compression. Bodies are buffered in memory up to max_body_bytes, so
large uploads want a different design.
MIT — see LICENSE.