Sans-IO HTTP/1.1 and multipart/form-data parsers written in C3. Push bytes in and get callbacks out. The library does not allocate on the heap, uses SIMD, and leaves buffering to 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.
- The consume / re-present contract works with a raw
recvloop,nova::framed'speek/consume, or an in-memory buffer. The library does not need to know which transport you use.
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 for the request line, headers, and body framing byContent-LengthandTransfer-Encoding: chunked(with trailers). The parser supports keep-alive and pipelining.http::multipart: multipart/form-data parser with 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; the parsers are covered by aTrackingAllocatortest. Bodies and file uploads stream through callbacks without buffering, straight to disk if you want. - Input checks: 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 the upload.
- 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.