Sans-IO HTTP/1.1 and multipart/form-data parsers written in C3. Push bytes in, get callbacks out — zero heap allocation, SIMD-accelerated, with all buffering owned by the caller.
http gives you two streaming parsers that never own a buffer and never
allocate on the hot path:
- You feed
executewhatever bytes you have; it fires callbacks with slices that point straight into your buffer and returns how many bytes it consumed. - Trailing bytes that form an incomplete line (or a partial delimiter) are left unconsumed; you re-present them prepended to the next chunk.
- That single consume / re-present contract is the seam any transport plugs
into — a raw
recvloop,nova::framed'speek/consume, or an in-memory buffer — with no transport knowledge baked into the library.
There is no per-message allocation: the parsers are fixed-size values, so the whole pipeline (parser + your read buffer) is constant memory regardless of request or upload size.
http::parser— HTTP/1.1 request parser: request line, headers, and body framing byContent-LengthandTransfer-Encoding: chunked(with trailers). Keep-alive and pipelining out of the box.http::multipart— multipart/form-data parser: preamble/epilogue, per-part headers, multiple files,Content-Typeper part, andContent-Dispositionname/filename/ RFC 5987filename*extraction, all into fixed inline storage.- SIMD scanning —
http::simdfinds line ends, delimiters and tokens with 16-byte (char[<16>]) vectors (SSE2 on x64, NEON on aarch64); no runtime feature detection. - Zero heap — neither parser takes an
Allocator; verified by aTrackingAllocatortest. Bodies and file uploads stream through callbacks without buffering, straight to disk if you want. - Hardened — rejects
Content-Length/Transfer-Encodingsmuggling, non-finalchunked, whitespace before the header colon, and overflowing Content-Length/chunk sizes; configurable line-size, header-count and part limits.
import std::io;
import http::parser;
fn void on_request_line(parser::Parser* p, char[] method, char[] target, char[] version, void* ud)
{
(void)io::printfn("%s %s %s", (String)method, (String)target, (String)version);
}
fn void on_header(parser::Parser* p, char[] name, char[] value, void* ud)
{
(void)io::printfn(" %s: %s", (String)name, (String)value);
}
fn void main()
{
parser::Settings settings = { .on_request_line = &on_request_line, .on_header = &on_header };
parser::Parser p;
p.init(&settings, null);
String request = "GET /hello HTTP/1.1\r\nHost: example.com\r\n\r\n";
usz consumed = p.execute((char[])request)!!;
(void)io::printfn("consumed %d / %d bytes", consumed, request.len);
}Prints:
GET /hello HTTP/1.1
Host: example.com
consumed 42 / 42 bytes
For a real transport, call execute repeatedly with whatever bytes arrive and
re-present data[result..] on the next call. Streaming a large upload to disk
is just writing each on_part_data (or on_body) slice to a file — the parser
holds none of it.
- Sans-IO, push model. The parser is a state machine over the bytes you hand it; it does no I/O and knows nothing about sockets or event loops.
- Caller-owned buffering. The parser keeps no buffer of its own. A line's
maximum length is whatever you are willing to re-present, and at most
delimiter_len - 1bytes are ever withheld between multipart calls. - Coalesced callbacks. Because a line is only emitted once its CRLF is seen,
the request-line fields and each header's name/value always arrive together,
so they are delivered in one call each (
on_request_line,on_header) rather than field-by-field. - Zero-allocation hot path. No
Allocatorin the API; the only memory is fixed inline storage for the multipart boundary and the current part's parsed disposition. - Contracts. Caller obligations are
@require/@ensure; recoverable failures arefaultvalues viaT?; every grammar, framing and limit violation has a named fault.
http is a C3 library bundle (http.c3l/). Put it on a dependency search path
and depend on the http library:
{
"dependency-search-paths": [ "lib" ],
"dependencies": [ "http" ]
}(place http.c3l/ under lib/), then in your code:
import http::parser; // HTTP/1.1 request parser
import http::multipart; // multipart/form-data parserFor a standalone file: c3c compile-run main.c3 --libdir lib --lib http.
Requires the c3c compiler.
c3c test # run the test suite (34 tests)
cd examples && c3c run request # run an example (see examples/)
cd fuzz && c3c build fuzz && ./build/fuzz # robustness fuzzer (no crash)
cd bench && c3c build bench && ./build/bench # benchmark vs picohttpparserRunnable programs in examples/ driving the parsers from an
in-memory buffer (the same consume / re-present loop a transport uses):
| Target | Shows |
|---|---|
request |
parse a request → request line, headers, body |
chunked-body |
stream a chunked body without buffering it |
upload |
multipart upload streamed straight to disk |
limits |
reject abusive requests via Config limits |
pipelining |
keep-alive: many requests on one parser |
See LICENSE.