module

RinhaDeBackend::HttpParser

100% Crystal HTTP/1.x request parser. Drop-in replacement for the phr_parse_request we used from h2o/picohttpparser — same external contract (pointer/length out-params, last_len slowloris fast-path, status codes: bytes consumed >= 0, -2 partial, -1 malformed).

Why we replaced the C version:

  • kills the FFI surface and the cc step in the build,
  • the SSE4.2 fast paths in upstream picohttpparser amortize over long URIs and long header values; the Rinha workload sees /fraud-score (12 B) and ~10 short headers from the k6 client,
  • we trust the rinha test harness to produce syntactically-valid bytes (the prod LB is HAProxy in mode tcp, byte-passthrough, so the parser sees client bytes verbatim), so we skip the per-byte tchar / CTL / DEL validation upstream picohttpparser does and just locate the structural delimiters (:, SP, CR, LF) via libc memchr. Glibc/musl memchr is heavily SIMD-tuned (SSE4.2 / AVX2 on x86_64), winning on every input length we care about.

Out of scope vs upstream (we never exercise these in this server):

  • response parsing (we never act as an HTTP client),
  • chunked transfer decoding (k6 always sends Content-Length),
  • obs-fold / multi-line header continuation (RFC 7230 obsoleted it and well-formed clients don't emit it).

Trust assumptions (i.e. things this parser does NOT detect):

  • non-token bytes inside a header name,
  • control bytes (other than CR/LF/HT) inside a header value,
  • control bytes inside the path. The Rinha test harness is well-formed; if it ever stops being so we would surface the issue downstream as a malformed JSON and answer the safe 200/0.0 fallback in HttpServer#handle_fraud_score.

Constants

COLON = 58_u8
CR = 13_u8
HT = 9_u8
LF = 10_u8
SP = 32_u8

Class methods

parse_request(buf : Pointer(UInt8), len : Int32, method : Pointer(Pointer(UInt8)), method_len : Pointer(Int32), path : Pointer(Pointer(UInt8)), path_len : Pointer(Int32), minor_version : Pointer(Int32), headers : Pointer(Header), num_headers : Pointer(Int32), last_len : Int32) : Int32

Top-level entry point. Drop-in for phr_parse_request.

Returns:

= 0 : number of bytes consumed (request-line + headers, body starts at this offset) -2 : input is partial, caller should keep reading -1 : malformed

Out-params populated on success: method.value, method_len.value -> METHOD slice path.value, path_len.value -> request-target slice minor_version.value -> 0 for HTTP/1.0, 1 for HTTP/1.1 headers[0..num_headers.value-1] -> name/value slices num_headers.value -> count of headers parsed

Source

Nested types