Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/avsm/httpz/llms.txt

Use this file to discover all available pages before exploring further.

The Parser module provides zero-allocation parser combinators for HTTP/1.1 parsing. Position is threaded explicitly through all combinators to avoid allocation. All combinators raise Parse_error on failure.

Types

pstate

type pstate = #{ buf : Base_bigstring.t; len : int16# }
Parser state - unboxed record holding buffer and length. Position is threaded explicitly through functions as a separate parameter.
buf
Base_bigstring.t
The buffer containing data to parse
len
int16#
Length of valid data in the buffer

Parse_error

exception Parse_error of Buf_read.status
Parse error exception with detailed status. Alias for Err.Parse_error.

Core Functions

make

val make : Base_bigstring.t -> len:int16# -> pstate
Create parser state from buffer and length.
buf
Base_bigstring.t
The buffer to parse
len
int16#
Number of valid bytes in buffer
Example:
let pst = Parser.make buf ~len:(i16 1024) in
(* Now use combinators *)

remaining

val remaining : pstate -> pos:int16# -> int16#
Get number of remaining bytes at current position.
pstate
pstate
Parser state
pos
int16#
Current position
remaining
int16#
Number of bytes from pos to end of buffer

at_end

val at_end : pstate -> pos:int16# -> bool
Check if at end of buffer (no remaining bytes).
pstate
pstate
Parser state
pos
int16#
Current position

Basic Combinators

All combinators take ~pos parameter and return new position.

peek_char

val peek_char : pstate -> pos:int16# -> char#
Peek current character without advancing position. Raises Parse_error with Partial status if at end. Example:
let c = Parser.peek_char pst ~pos in
if c =. '\r' then (* handle CR *)

peek_at

val peek_at : pstate -> pos:int16# -> int16# -> char#
Peek character at offset from current position. Raises Parse_error with Partial if out of bounds.
offset
int16#
Offset from current position

char

val char : char# -> pstate -> pos:int16# -> int16#
Match single character, return new position. Raises Parse_error with Partial if at end or Malformed if character doesn’t match.
expected
char#
Character to match
Example:
let pos = Parser.char ' ' pst ~pos in (* Match space *)

string

val string : string -> pstate -> pos:int16# -> int16#
Match literal string, return new position. Raises Parse_error with Partial or Malformed.
expected
string
String to match
Example:
let pos = Parser.string "HTTP/" pst ~pos in

take_while

val take_while : 
  (char# -> bool) -> 
  pstate -> 
  pos:int16# -> 
  #(Span.t * int16#)
Take characters while predicate holds, return span and new position. Returns empty span if predicate fails immediately.
predicate
char# -> bool
Predicate function to test each character
span
Span.t
Span covering matched characters
pos
int16#
New position after matched characters
Example:
let #(digits, pos) = Parser.take_while is_digit pst ~pos in
let value = Span.parse_int64 pst.#buf digits in

skip_while

val skip_while : (char# -> bool) -> pstate -> pos:int16# -> int16#
Skip characters while predicate holds, return new position.

take

val take : int16# -> pstate -> pos:int16# -> #(Span.t * int16#)
Take exactly n characters as span, return span and new position. Raises Parse_error with Partial if not enough bytes.
n
int16#
Number of characters to take

skip

val skip : int16# -> pstate -> pos:int16# -> int16#
Skip exactly n characters, return new position. Raises Parse_error with Partial if not enough bytes.

satisfy

val satisfy : 
  (char# -> bool) -> 
  pstate -> 
  pos:int16# -> 
  #(char# * int16#)
Match character satisfying predicate, return character and new position. Raises Parse_error if predicate fails or at end. Example:
let #(digit, pos) = Parser.satisfy is_digit pst ~pos in

optional

val optional : 
  (pstate -> pos:int16# -> #('a * int16#)) -> 
  pstate -> 
  pos:int16# -> 
  #('a or_null * int16#)
Try parser, return Null and original position on failure. Returns #('a) (unboxed option) on success.
parser
pstate -> pos:int16# -> #('a * int16#)
Parser to try
Example:
let #(version_opt, pos) = Parser.optional Parser.http_version pst ~pos in
match version_opt with
| Null -> (* No version found *)
| #(version) -> (* Use version *)

HTTP-Specific Combinators

crlf

val crlf : pstate -> pos:int16# -> int16#
Match CRLF sequence (\r\n), return new position. Raises Parse_error if not found. Example:
let pos = Parser.crlf pst ~pos in (* Consume line ending *)

sp

val sp : pstate -> pos:int16# -> int16#
Match single space character (SP), return new position.

token

val token : pstate -> pos:int16# -> #(Span.t * int16#)
Take HTTP token characters (for method, header names), return span and new position. Must be non-empty. Raises Malformed if empty. Token characters are: alphanumeric and !#$%&'*+-.^_|~` Example:
let #(method_span, pos) = Parser.token pst ~pos in

ows

val ows : pstate -> pos:int16# -> int16#
Skip optional whitespace (OWS = SP / HTAB), return new position. OWS stands for “optional whitespace” in HTTP grammar.

http_version

val http_version : pstate -> pos:int16# -> #(Version.t * int16#)
Parse HTTP version (HTTP/1.0 or HTTP/1.1), return version and new position. Raises Parse_error with Invalid_version if malformed. Example:
let #(version, pos) = Parser.http_version pst ~pos in
match version with
| Version.Http_1_0 -> (* HTTP/1.0 *)
| Version.Http_1_1 -> (* HTTP/1.1 *)

parse_method

val parse_method : pstate -> pos:int16# -> #(Method.t * int16#)
Parse HTTP method from token, return method enum and new position. Raises Parse_error with Invalid_method if unknown method.
method
Method.t
Parsed method: Get, Head, Post, Put, Delete, Connect, Options, Trace, or Patch
Example:
let #(meth, pos) = Parser.parse_method pst ~pos in

parse_target

val parse_target : pstate -> pos:int16# -> #(Span.t * int16#)
Parse request target, return span and new position. Raises Parse_error with Invalid_target if empty.

request_line

val request_line : 
  pstate -> 
  pos:int16# -> 
  #(Method.t * Span.t * Version.t * int16#)
Parse complete request line: METHOD SP target SP version CRLF.
method
Method.t
HTTP method
target
Span.t
Request target span
version
Version.t
HTTP version
pos
int16#
Position after request line
Example:
let pst = Parser.make buf ~len in
let #(meth, target, version, pos) = Parser.request_line pst ~pos:(i16 0) in
let target_str = Span.to_string pst.#buf target in

parse_header

val parse_header : 
  pstate -> 
  pos:int16# -> 
  #(Header_name.t * Span.t * Span.t * int16#)
Parse a single header line: name: value CRLF.
header_name
Header_name.t
Parsed header name enum (or Other for unknown headers)
name_span
Span.t
Span of header name (meaningful when header_name is Other)
value_span
Span.t
Span of header value (leading/trailing OWS trimmed)
pos
int16#
Position after header line
Example:
let #(name, name_span, value_span, pos) = Parser.parse_header pst ~pos in
match name with
| Header_name.Content_length ->
  let len = Span.parse_int64 pst.#buf value_span in
  (* ... *)
| Header_name.Other ->
  if Span.equal_caseless pst.#buf name_span "x-custom" then
    let value = Span.to_string pst.#buf value_span in
    (* Handle custom header *)
| _ -> ()

is_headers_end

val is_headers_end : pstate -> pos:int16# -> bool
Check if at end of headers (CRLF at current position, indicating empty line). Example:
if Parser.is_headers_end pst ~pos then
  let pos = Parser.end_headers pst ~pos in
  (* pos now points to body *)

end_headers

val end_headers : pstate -> pos:int16# -> int16#
Skip the empty line at end of headers (consume final CRLF), return new position pointing to body.

Usage Pattern

The typical pattern for using parser combinators:
open Httpz

let parse_request buf ~len ~limits =
  try
    let pst = Parser.make buf ~len in
    
    (* Parse request line *)
    let #(meth, target, version, pos) = 
      Parser.request_line pst ~pos:(i16 0) 
    in
    
    (* Parse headers in a loop *)
    let rec parse_headers pos acc =
      if Parser.is_headers_end pst ~pos then
        let body_off = Parser.end_headers pst ~pos in
        #(body_off, List.rev acc)
      else
        let #(name, name_span, value_span, pos) = 
          Parser.parse_header pst ~pos 
        in
        let hdr = { Header.name; name_span; value = value_span } in
        parse_headers pos (hdr :: acc)
    in
    
    let #(body_off, headers) = parse_headers pos [] in
    
    (* Build request *)
    Ok #(meth, target, version, body_off, headers)
    
  with Parser.Parse_error status ->
    Error status

Error Handling

All combinators raise Parse_error exception with specific status:
  • Partial - Need more data (at end of buffer)
  • Malformed - Invalid syntax
  • Invalid_method - Unknown HTTP method
  • Invalid_target - Empty or invalid target
  • Invalid_version - Malformed HTTP version
  • Invalid_header - Malformed header line
Handle errors using try/catch:
try
  let #(result, pos) = Parser.some_combinator pst ~pos in
  (* Success *)
with Parser.Parse_error status ->
  match status with
  | Partial -> need_more_data ()
  | Malformed -> send_400 ()
  | _ -> send_error ()

Performance Notes

  • Zero allocation - Position threading avoids closure allocation
  • Unboxed types - Uses char#, int16#, and unboxed tuples
  • Explicit position - No hidden state, enables optimization
  • Single-pass - Parse data in one forward pass
  • Stack results - All results returned with @ local annotation