HTTP parser
August 23, 2026 · View on GitHub
proto/http.nim is an incremental, zero-copy HTTP/1.1 request parser. It
materializes strings lazily from byte offsets — nothing is copied until you ask
for it — and it drives streaming bodies, chunked transfer encoding and
multipart.
Most users never touch the parser directly: the HTTP server feeds it for you and
hands you an HttpRequest (see requests). This page is for
understanding it, or for using it standalone.
Creating a parser
var p = newHttpParser(initialBufSize = 4096)
Public parser fields: buf, bufLen, maxBodySize, maxStreamBodySize,
headerEnd, contentLength, connectionClose, expectContinue,
onBodyData (streaming callback), methodCache, phase.
Feeding bytes
let phase = p.feed(data) # openArray[byte] or string
The parser is incremental: feed as many or as few bytes as arrive. It tracks a
ParsePhase: PhaseRequestLine, PhaseHeaders, PhaseBody,
PhaseComplete, PhaseError.
p.isComplete() # reached PhaseComplete
p.isError() # reached PhaseError
p.error() # the HttpCode that caused the error
p.phase() # current phase
p.setError(Http400) # force an error state (rejects the request)
reset clears the parser; resetForNext keeps reusable state for the next
request on a keep-alive connection.
Zero-copy request fields
Before the request is complete you can peek:
p.peekMethod() # HttpMethod
p.peekPath() # lent string
p.peekContentType() # lent string
Once complete, materialize a request object:
let req = p.getRequest() # HttpRequest — see requests.md
getRemainingData(p) returns bytes beyond the current request (pipelining).
Streaming bodies
p.onBodyData = proc(data: openArray[byte]; done: bool) {.closure.} is invoked
as body bytes are parsed; done signals the final chunk. Set it before
feeding body bytes.
p.onBodyData = proc(data: openArray[byte]; done: bool) =
write(f, data) # stream to disk without buffering the whole body
Limits
Constants enforced by the parser/server:
| Constant | Value |
|---|---|
MaxHeaderSize | 8192 |
MaxRequestLine | 8192 |
MaxHeaders | 100 |
DefaultBodyBuf | 65536 |
MaxStreamBodySize | 512 MB |
See security for how these protect against DoS.
Chunked trailers
Chunked bodies may carry trailers — Name: value lines after the zero-size
chunk (RFC 9112 §7.1.3). The parser accepts them incrementally, enforces the
same strictness as headers (CRLF only, colon required, no obs-fold,
MaxHeaderSize per section, MaxHeaders per count; violations are 400/431),
and exposes them once complete:
if parser.isComplete():
if parser.hasTrailers():
for (name, value) in parser.getTrailers():
echo name, ": ", value
let crc = req.getTrailer("X-Crc32") # case-insensitive lookup, "" if absent
Body streaming on requests
For fully parsed requests, the BodyStream API in
requests is the higher-level way to consume bodies in chunks.
API reference
Full signatures: HTTP parser API. Related: requests, server.