roadrunner_http (roadrunner v0.6.0)
View SourceProtocol-version-agnostic HTTP semantics shared by HTTP/1.1
(roadrunner_http1) and HTTP/2 (roadrunner_http2_*) modules.
What lives here is RFC 9110 semantics — types and helpers whose
meaning doesn't depend on wire framing — not RFC 9112 syntax.
The HTTP/1.1 wire codec (request-line / header / chunked
parsers, status-line + CRLF response encoder) stays in
roadrunner_http1. HTTP/2 frame codec + HPACK live in their
own modules.
Items here:
- Header list shape:
[{binary(), binary()}]. - HTTP status codes:
100..599and the redirect subset. - Protocol version tuple:
{Major, Minor}. - IMF-fixdate formatter (
http_date_now/0for the current time,format_http_date/1for an arbitrary posix timestamp) for theDateresponse header per RFC 9110 §5.6.7 and theLast-Modifiedresponse header used by the static handler. - Header field-value safety (RFC 9110 §5.5): reject CR/LF/NUL in a
header name or value before it reaches the wire
(
check_header_safe/2), shared by every version's response path. - Connection-specific field stripping (RFC 9113 §8.2.2 / RFC 9114
§4.2): drop the hop-by-hop fields HTTP/2 and HTTP/3 MUST NOT
generate from a response field section
(
strip_connection_specific_fields/1), or do it fused with the field-value check in a single pass (strip_connection_specific_fields_safe/1); HTTP/1.1 honours these fields, so it does not strip.
roadrunner_http1 and roadrunner_req re-export the primitive
types as aliases so existing callers keep compiling unchanged.
The request map shape lives in roadrunner_req alongside the
accessors that operate on it.
Summary
Functions
Inject the framework's automatic response headers for an HTTP/1 or
HTTP/2 (TCP) response: Date always (RFC 9110 §6.6.1) plus Alt-Svc
advertising the listener's HTTP/3 endpoint (RFC 7838) when it co-serves
h3 on a fixed port. The caller passes the precomputed Alt-Svc value
(cached on the connection loop record), or undefined when no h3 is
co-served. HTTP/3 responses use with_date/1 directly — a client
already on h3 needs no Alt-Svc.
Validate that a header name or value contains no CR, LF, or NUL —
the bytes that would let an attacker who controls the value inject
new headers (or terminate the header block early). Crashes with
{header_injection, Kind, Bin} when an unsafe byte is present.
Format a posix timestamp (seconds since epoch) as an IMF-fixdate
per RFC 9110 §5.6.7. Same shape as http_date_now/0 but for an
explicit timestamp — used by the static file handler to emit the
Last-Modified header for a file's mtime.
Format the current UTC time as an IMF-fixdate per RFC 9110 §5.6.7
— the canonical HTTP Date header format, e.g.
Sun, 06 Nov 1994 08:49:37 GMT. Used by the dispatch layer to
auto-inject the Date response header per RFC 9110 §6.6.1.
Drop connection-specific header fields from an HTTP/2 or HTTP/3
response field section. RFC 9113 §8.2.2 and RFC 9114 §4.2 forbid
generating connection, keep-alive, proxy-connection,
transfer-encoding, or upgrade over those protocols — they are
hop-by-hop fields (RFC 9110 §7.6.1) meaningful only on an HTTP/1.1
connection. A handler shared across protocols may set one (e.g.
connection: close, idiomatic on h1); stripping it on h2/h3 keeps that
handler working while staying conformant, since the framework never
puts the field on the wire. HTTP/1.1 honours these fields, so its
response path does not strip.
Single-pass combination of check_header_safe/2 and
strip_connection_specific_fields/1 for the response paths that crash
on injection (the HTTP/2 conn loop and the HTTP/3 trailer path): in one
traversal it rejects CR/LF/NUL in any name or value (crashing with
{header_injection, Kind, Bin}, like check_header_safe/2) and drops
the connection-specific fields h2/h3 MUST NOT generate. HTTP/3 response
headers answer 500 on injection instead of crashing, so they run the
non-crashing check and strip_connection_specific_fields/1 separately.
Inject a Date response header (RFC 9110 §6.6.1) unless the handler
already set one. Shared by the HTTP/1, HTTP/2, and HTTP/3 response
paths so every response carries Date from the one cached clock
(http_date_now/0). RFC 9110 makes Date a MUST on 2xx/3xx/4xx and
a MAY on 1xx/5xx, so injecting it unconditionally is conformant.
Types
Functions
Inject the framework's automatic response headers for an HTTP/1 or
HTTP/2 (TCP) response: Date always (RFC 9110 §6.6.1) plus Alt-Svc
advertising the listener's HTTP/3 endpoint (RFC 7838) when it co-serves
h3 on a fixed port. The caller passes the precomputed Alt-Svc value
(cached on the connection loop record), or undefined when no h3 is
co-served. HTTP/3 responses use with_date/1 directly — a client
already on h3 needs no Alt-Svc.
-spec check_header_safe(binary(), name | value) -> ok.
Validate that a header name or value contains no CR, LF, or NUL —
the bytes that would let an attacker who controls the value inject
new headers (or terminate the header block early). Crashes with
{header_injection, Kind, Bin} when an unsafe byte is present.
Public so any response path emitting a single header (e.g.
roadrunner_stream_response for chunked-response trailers) can run
the check before writing to the wire.
Format a posix timestamp (seconds since epoch) as an IMF-fixdate
per RFC 9110 §5.6.7. Same shape as http_date_now/0 but for an
explicit timestamp — used by the static file handler to emit the
Last-Modified header for a file's mtime.
-spec http_date_now() -> binary().
Format the current UTC time as an IMF-fixdate per RFC 9110 §5.6.7
— the canonical HTTP Date header format, e.g.
Sun, 06 Nov 1994 08:49:37 GMT. Used by the dispatch layer to
auto-inject the Date response header per RFC 9110 §6.6.1.
Built via direct bit-syntax binary construction rather than
io_lib:format/2 because the shape is fixed (RFC 9110 mandates
exact widths and the day/month abbreviations) and this function
runs on the response hot path.
Cached per process in the process dictionary, keyed by the current
Posix second: the formatted binary is identical for every response
a process emits within the same second, so we recompute it only when
the second ticks over. Per-process rather than via persistent_term
because the value changes every second, and a persistent_term:put
that frequent forces a global scan of every process heap on the
response hot path; the per-process cache pays a cheap dictionary read
instead, and reformats at most once per second per process: once per
connection on h1/h2, once per request on h3 (its stream workers are
per-request).
Drop connection-specific header fields from an HTTP/2 or HTTP/3
response field section. RFC 9113 §8.2.2 and RFC 9114 §4.2 forbid
generating connection, keep-alive, proxy-connection,
transfer-encoding, or upgrade over those protocols — they are
hop-by-hop fields (RFC 9110 §7.6.1) meaningful only on an HTTP/1.1
connection. A handler shared across protocols may set one (e.g.
connection: close, idiomatic on h1); stripping it on h2/h3 keeps that
handler working while staying conformant, since the framework never
puts the field on the wire. HTTP/1.1 honours these fields, so its
response path does not strip.
Single-pass combination of check_header_safe/2 and
strip_connection_specific_fields/1 for the response paths that crash
on injection (the HTTP/2 conn loop and the HTTP/3 trailer path): in one
traversal it rejects CR/LF/NUL in any name or value (crashing with
{header_injection, Kind, Bin}, like check_header_safe/2) and drops
the connection-specific fields h2/h3 MUST NOT generate. HTTP/3 response
headers answer 500 on injection instead of crashing, so they run the
non-crashing check and strip_connection_specific_fields/1 separately.
Inject a Date response header (RFC 9110 §6.6.1) unless the handler
already set one. Shared by the HTTP/1, HTTP/2, and HTTP/3 response
paths so every response carries Date from the one cached clock
(http_date_now/0). RFC 9110 makes Date a MUST on 2xx/3xx/4xx and
a MAY on 1xx/5xx, so injecting it unconditionally is conformant.