A minimal, high-performance Julia wrapper around the picohttpparser C library.
This package provides extremely fast HTTP parsing by using zero-copy StringViews. Instead of allocating new Strings for every header name and value, it returns views into the original unmodified buffer, significantly reducing GC pressure and memory usage.
- Zero-Copy Parsing: Uses
StringViews.jlto return views into your buffer. - Streaming / Incremental Support: Parse requests as they arrive in chunks without rescanning.
- Chunked Transfer Decoding: High-performance in-place chunked decoding.
- Battle-Tested Backend: Bindings to the widely used
picohttpparserC library.
pkg> add PicoHTTPParserusing PicoHTTPParserNote: Input must be a
Vector{UInt8}. The returnedRequestobject holds views into this buffer, so you must keep the buffer alive while using the request.
# Create a mutable buffer (Vector{UInt8})
data = """GET /index.html HTTP/1.1\r
Host: example.com\r
User-Agent: Julia\r
\r
""" |> Vector{UInt8}
# Parse the request
# parse_request(buf, last_len; max_headers)
req = parse_request(data)
@show req.method # "GET" (StringView)
@show req.path # "/index.html"
@show req.minor_version # 1
@show req.headers # Vector{Pair{StringView, StringView}}
@show req.body # SubArray (View of the body)If you receive data in chunks (e.g., disjoint TCP reads), you can use the last_len parameter to tell the parser where you left off. This prevents rescanning bytes that were already confirmed to be part of an incomplete header section.
# 1. Initialize a buffer
buf = Vector{UInt8}()
prev_len = 0
# 2. Receive Part 1 (Incomplete)
part1 = "GET /async HTTP/1.1\r\nUser-A"
append!(buf, Vector{UInt8}(part1))
# Try to parse. Pass prev_len (0 initially).
req = parse_request(buf, prev_len)
# req === nothing (Incomplete)
# Update prev_len so we don't rescan the first part
prev_len = length(buf)
# 3. Receive Part 2 (Complete)
part2 = "gent: Julia\r\n\r\n"
append!(buf, Vector{UInt8}(part2))
# Parse again from where we left off
req = parse_request(buf, prev_len)
if req !== nothing
println("Parsed: $(req.method) $(req.path)")
endThe decode_chunked! function performs in-place decoding. It collapses the chunk metadata and moves the actual data to the front of the buffer, returning a view of the valid data.
# "Wiki" encoded in chunks: "4\r\nWiki\r\n0\r\n\r\n"
raw_chunked = "4\r\nWiki\r\n0\r\n\r\n" |> Vector{UInt8}
decoder = ChunkedDecoder()
# Modifies 'raw_chunked' in-place!
result = decode_chunked!(decoder, raw_chunked)
@show result.done # true
@show String(result.data) # "Wiki"Any contributions are welcome!