AGENTS.md
June 30, 2026 · View on GitHub
Working notes for agents modifying this repository. For the design read
ARCHITECTURE.md; for usage read README.md. This file covers the repo layout,
how to build and test, the invariants you must not break, and the traps that are
easy to fall into.
Repo map
http_parser.h Declares the API (init, settings, execute, helpers) and the http_parser / http_parser_settings structs. Header. Verbatim from Xapiand.
http_parser_enums.h The method / status / errno / state / header-state enums, defined with enum.h's ENUM() macro. Includes "enum.h". Verbatim from Xapiand.
http_parser.cc The streaming state machine: http_parser_execute() plus helpers. Verbatim from Xapiand except for the .c -> .cc rename. Includes "likely.h".
likely.h Self-contained likely()/unlikely() branch hints. Vendored on extraction (no config.h coupling).
test/test.cc Runnable smoke test: GET, POST+body, byte-by-byte streaming, chunked across two buffers, malformed method, keep-alive.
examples/demo.cc A runnable tour (not a test).
CMakeLists.txt STATIC library `http_parser` (+ alias http_parser::http_parser); FetchContent enum-reflection; CTest test `http_parser`.
LICENSE MIT. NGINX/Joyent for the parser; Dubalu LLC for the Xapiand additions and extraction wiring.
README.md What it is, install, usage, API.
ARCHITECTURE.md Internal design of the streaming state machine, callbacks, body handling, enums.
This is not header-only. http_parser.cc must be compiled and linked; the
headers only declare the API and the enums. The CMake target is a STATIC library
that links enum-reflection so "enum.h" resolves.
Build and run the test
cmake -B build && cmake --build build && ctest --test-dir build
Configure with the Homebrew LLVM compiler if you want the family's reference toolchain:
cmake -B build -DCMAKE_CXX_COMPILER=$(brew --prefix llvm)/bin/clang++
The first configure fetches enum-reflection over the network (FetchContent,
GIT_TAG main), and that pulls its own transitive deps (perfect-hash, hashes,
static-string, char-classify). Expected output ends with
all http-parser tests passed, exit 0. The test target is http_parser_test; the
registered CTest name is http_parser. The test and demo are only added when this
repo is the top-level project (CMakeLists.txt), so consumers vendoring it via
FetchContent won't build them.
Dependency
http_parser_enums.h includes "enum.h" and defines its enums with the ENUM()
macro. That header lives in the sibling
enum-reflection library, pulled in by
CMake and linked PUBLIC so the include path resolves (the public header chain
http_parser.h -> http_parser_enums.h -> "enum.h" reaches consumers, hence
PUBLIC). We track its tip with GIT_TAG main, like the rest of the family. This
is the library's only external dependency. The implementation's other include,
"likely.h", is satisfied by the vendored copy in this repo, not a dependency.
Conventions
- C++20. The target requests
cxx_std_20PUBLICto stay uniform with the sibling libraries. Don't drop the target below it. - The implementation is compiled as C++. That is deliberate: it is what makes
enum.h's C++ machinery (and so the enum-reflection dependency) load-bearing, and it matches the C++20 family. The file is namedhttp_parser.ccrather than.cso the C++ compile is first-class, not the deprecated "treat .c as C++" path. - Header filenames are stable.
http_parser.handhttp_parser_enums.hkeep their original Xapiand names so a consumer that already#includes them just needs this repo on the include path. Don't rename the headers. - Tabs for indentation in new code, double quotes in code, no em dashes in prose.
- MIT-licensed; keep the existing copyright headers on the source files (the
NGINX/Joyent header on
http_parser.h/http_parser_enums.h/http_parser.cc, the Dubalu header onlikely.h).
Load-bearing invariants
- The data callbacks can fire more than once per logical value.
on_url,on_header_field,on_header_value, andon_bodyeach deliver slices of the caller's buffer and may be called repeatedly as a run straddles buffer boundaries. Any consumer (including the test and demo here) must append, not assign, and treat a header-field callback after a header-value callback as the start of a new pair. Don't write a test that assumes a value arrives whole. content_lengthcounts down, it is not the header value. It is loaded fromContent-Lengthand decremented as body bytes are delivered, reaching0aton_message_complete. Reading it after a complete parse gives0. The test asserts exactly this; don't "fix" it to expect the header number.http_errno_name()returns theHPE_-prefixed token. The string table is generated as"HPE_" #n, so the name is e.g."HPE_INVALID_METHOD", not"INVALID_METHOD". Assertions must match the prefixed form.- The parser is streaming;
execute()returns bytes consumed. On malformed input it stops and the return is short of the input length, withHTTP_PARSER_ERRNO(parser)set. Check the errno after every call, not just the return. - The enums come from
enum.h'sENUM()macro. Don't replace them with plain C enums or you break the reflective accessors and the dependency's reason to exist. The X-macro maps (HTTP_METHOD_MAPetc.) feed both the enums and the string tables, so edit the map, not the two outputs separately.
How to extend
- Add a request method. Append to
HTTP_METHOD_MAPinhttp_parser_enums.h(the X-macro feeds both thehttp_methodenum andmethod_strings). The parser's method-matching trie inhttp_parser.ccthen needs the new verb wired into its character dispatch; this is the fiddly part, mirror an existing verb of the same first letters. - Always extend the smoke test.
test/test.ccis the only executable check. Add a case for any new behavior and assert the exact parsed method / url / headers / body, following the append-based collector already there.
Traps
- Compiling the implementation as C silently changes the enums.
enum.hgates its C++ machinery behind#ifdef __cplusplus; built as C,ENUM()collapses to a plain enum and the reflective accessors and the enum-reflection dependency vanish. Keep the source.ccand the target a CXX target. likely.his vendored, not the dependency. It is a tiny self-contained branch-hint header. Don't try to resolve it against enum-reflection or reintroduce Xapiand'sconfig.h-coupled version.- The headers still carry the C
extern "C"and<sys/types.h>baggage from upstream. That is intentional for ABI compatibility with the original; leave it.
Standalone vs. Xapiand
This is a standalone extraction from
Xapiand, which itself vendored and adapted
the Joyent/Node.js http_parser. The headers and the implementation were copied
from Xapiand's in-tree copy. The extraction deltas are small and mechanical: the
one local include "enum.h" is resolved against the sibling enum-reflection
library through CMake, the "likely.h" include is satisfied by a vendored
self-contained copy (dropping Xapiand's config.h coupling), and the
implementation file was renamed http_parser.c -> http_parser.cc. No parser
logic changed; any edit here should stay reconcilable with upstream as a plain
diff.
Consumed by Kronuz/http (the HTTP application
layer): it parses with this fork rather than a stricter parser like llhttp because
this one accepts arbitrary request methods, which Xapiand's custom REST verbs
(COUNT, INFO, DUMP, RESTORE, …) require.
Planned refresh (separate commit): the fork is pinned at version 2.7.1; the Joyent upstream's final release is 2.9.4. A future change rebases the fork onto 2.9.4 to pick up ~2.5 years of security/correctness fixes, re-applying the small extraction deltas above. Keep edits reconcilable so that rebase stays a plain diff.