nhttp_msg (nhttp_lib v1.0.0)
View SourceShared message-level helpers for the HTTP/2 (RFC 9113) and HTTP/3 (RFC 9114) state machines.
These functions deal with the parts of a request/response that are
identical on both wire formats once the framing layer has produced a
list of {Name, Value} pseudo + regular header pairs:
- digit/length parsing (
is_digits/1,parse_content_length/1) - content-length extraction and end-of-stream validation
- pseudo-header projection (
extract_request_pseudo/1,extract_response_pseudo/1) - the
hostfallback used when:authorityis empty - trailers shape validation
The module is intentionally protocol-agnostic: errors are returned as plain atoms and the calling protocol wraps them into the relevant wire error format (atom for H2, descriptive binary for H3).
Summary
Functions
Assemble the canonical nhttp_lib:request/0 map from the pseudo
plus regular header list emitted by the protocol decoders.
Assemble the canonical nhttp_lib:response/0 map. Version is
stamped onto the map; reason is the empty binary (HTTP/2 and HTTP/3
do not carry a reason phrase, RFC 9113 §8.3.2, RFC 9114 §4.3.2).
When both :authority and Host are present they must carry the
same value (RFC 9113 §8.3.1, RFC 9114 §4.3.1). An undefined
component is treated as absent. Returns {error, protocol_error} on
mismatch.
Validate the RFC 8441 / 9220 Extended CONNECT pseudo-header
combination. The four arguments are :method, :protocol,
:authority and the local settings map (peer settings on the server
side). The peer must advertise SETTINGS_ENABLE_CONNECT_PROTOCOL=1
to enable the feature.
Returns ok when the combination is acceptable (including the
common case where :protocol is absent), or one of three reasons
that the caller maps to the protocol-appropriate error payload
Find the first content-length header in Headers and parse it.
Returns undefined when the header is absent or has a non-binary
value (defensive guard kept from the H2 implementation).
Project the pseudo-header part of a request header list into typed
components and return the regular headers in their original order.
Unknown pseudo-headers (<<":", _>>) are dropped. The caller is
expected to have already rejected them at validation time.
Project :status out of a response header list, returning the
integer status and the regular headers in their original order.
Unknown pseudo-headers are dropped (already rejected at validation).
Return the host header value or <<>> when missing. Used as the
authority fallback when :authority is empty (RFC 9113 §8.3.1,
RFC 9114 §4.3.1).
True iff every byte in Bin is an ASCII digit ($0..$9). Used by
parse_content_length/1 to reject negative or otherwise malformed
values before reaching binary_to_integer/1.
Parse a content-length header value. Returns the integer for a
non-negative decimal binary, or undefined for the empty binary or
any non-digit input. Never raises.
Check that the bytes received so far are consistent with the
advertised content-length. undefined always passes. On the final
frame the counts must match exactly; on intermediate frames the count
must not exceed the advertised length.
Run the protocol-agnostic request header validation FSM (RFC 9113 §8.2 / RFC 9114 §4.2) and return a normalized intermediate shape that protocol-specific post-passes can inspect. The FSM enforces the rules that are byte-identical across HTTP/2 and HTTP/3
Trailers must not contain pseudo-headers (RFC 9113 §8.1, RFC 9114
§4.1). Returns ok or {error, pseudo_in_trailers}. Callers map the
atom to their protocol's error payload.
The :scheme pseudo-header must be http or https on the wire
(RFC 9113 §8.3.1, RFC 9114 §4.3.1). Returns {error, protocol_error}
on any other value (including undefined). Callers map the atom to
their preferred error payload.
Types
-type content_length_error() :: content_length_mismatch.
-type extended_connect_error() :: missing_authority | bad_method | not_enabled.
-type request_shape_error() ::
duplicate_pseudo | unknown_pseudo | pseudo_after_regular | forbidden_connection_header |
multiple_host_headers | missing_required_pseudo | bad_wire_scheme | authority_host_mismatch.
-type trailers_error() :: pseudo_in_trailers.
Functions
-spec build_request(nhttp_lib:version(), nhttp_lib:peer() | undefined, nhttp_lib:headers()) -> nhttp_lib:request().
Assemble the canonical nhttp_lib:request/0 map from the pseudo
plus regular header list emitted by the protocol decoders.
Version is stamped into the returned map; Peer is included as the
peer key when not undefined. Authority falls back to the host
header when :authority is empty (RFC 9113 §8.3.1, RFC 9114 §4.3.1).
The Extended CONNECT :protocol (RFC 8441 / 9220) is added when
present.
-spec build_response(nhttp_lib:version(), nhttp_lib:headers()) -> nhttp_lib:response().
Assemble the canonical nhttp_lib:response/0 map. Version is
stamped onto the map; reason is the empty binary (HTTP/2 and HTTP/3
do not carry a reason phrase, RFC 9113 §8.3.2, RFC 9114 §4.3.2).
-spec check_authority_host_match(binary() | undefined, binary() | undefined) -> ok | {error, protocol_error}.
When both :authority and Host are present they must carry the
same value (RFC 9113 §8.3.1, RFC 9114 §4.3.1). An undefined
component is treated as absent. Returns {error, protocol_error} on
mismatch.
-spec check_extended_connect(binary() | undefined, binary() | undefined, binary() | undefined, map()) -> ok | {error, extended_connect_error()}.
Validate the RFC 8441 / 9220 Extended CONNECT pseudo-header
combination. The four arguments are :method, :protocol,
:authority and the local settings map (peer settings on the server
side). The peer must advertise SETTINGS_ENABLE_CONNECT_PROTOCOL=1
to enable the feature.
Returns ok when the combination is acceptable (including the
common case where :protocol is absent), or one of three reasons
that the caller maps to the protocol-appropriate error payload:
missing_authority: CONNECT without:authority.not_enabled: peer has not advertisedenable_connect_protocol.bad_method::protocolpresent with a method other than CONNECT.
-spec extract_content_length(nhttp_lib:headers()) -> non_neg_integer() | undefined.
Find the first content-length header in Headers and parse it.
Returns undefined when the header is absent or has a non-binary
value (defensive guard kept from the H2 implementation).
-spec extract_request_pseudo(nhttp_lib:headers()) -> {nhttp_lib:method() | undefined, binary(), nhttp_lib:scheme(), nhttp_lib:authority(), binary() | undefined, nhttp_lib:headers()}.
Project the pseudo-header part of a request header list into typed
components and return the regular headers in their original order.
Unknown pseudo-headers (<<":", _>>) are dropped. The caller is
expected to have already rejected them at validation time.
-spec extract_response_pseudo(nhttp_lib:headers()) -> {nhttp_lib:status() | 0, nhttp_lib:headers()}.
Project :status out of a response header list, returning the
integer status and the regular headers in their original order.
Unknown pseudo-headers are dropped (already rejected at validation).
-spec host_header_or_empty(nhttp_lib:headers()) -> nhttp_lib:authority().
Return the host header value or <<>> when missing. Used as the
authority fallback when :authority is empty (RFC 9113 §8.3.1,
RFC 9114 §4.3.1).
True iff every byte in Bin is an ASCII digit ($0..$9). Used by
parse_content_length/1 to reject negative or otherwise malformed
values before reaching binary_to_integer/1.
-spec parse_content_length(binary()) -> non_neg_integer() | undefined.
Parse a content-length header value. Returns the integer for a
non-negative decimal binary, or undefined for the empty binary or
any non-digit input. Never raises.
-spec validate_content_length(non_neg_integer() | undefined, non_neg_integer(), nhttp_lib:fin()) -> ok | {error, content_length_error()}.
Check that the bytes received so far are consistent with the
advertised content-length. undefined always passes. On the final
frame the counts must match exactly; on intermediate frames the count
must not exceed the advertised length.
-spec validate_request_pseudo_shape(nhttp_lib:headers()) -> {ok, request_shape()} | {error, request_shape_error()}.
Run the protocol-agnostic request header validation FSM (RFC 9113 §8.2 / RFC 9114 §4.2) and return a normalized intermediate shape that protocol-specific post-passes can inspect. The FSM enforces the rules that are byte-identical across HTTP/2 and HTTP/3:
- Pseudo-header block precedes the regular fields.
- No duplicate pseudo-headers and no unknown pseudo names.
- No hop-by-hop / connection-specific fields (
Connection,Keep-Alive,Proxy-Connection,Transfer-Encoding,Upgrade). - At most one
Hostfield. :method,:schemeand a non-empty:pathare present.:schemeishttporhttpson the wire.- When both
:authorityandHostare present they must match. Protocol-specific rules are deferred (TE policy per RFC 9113 §8.2.2 vs RFC 9114 §4.2, HTTP/3's "authority-requiring scheme needs:authorityorHost" rule, and Extended CONNECT settings). Callers userequest_shape()plus their own settings to finish validation.
-spec validate_trailers(nhttp_lib:headers()) -> ok | {error, trailers_error()}.
Trailers must not contain pseudo-headers (RFC 9113 §8.1, RFC 9114
§4.1). Returns ok or {error, pseudo_in_trailers}. Callers map the
atom to their protocol's error payload.
-spec validate_wire_scheme(binary() | undefined) -> ok | {error, protocol_error}.
The :scheme pseudo-header must be http or https on the wire
(RFC 9113 §8.3.1, RFC 9114 §4.3.1). Returns {error, protocol_error}
on any other value (including undefined). Callers map the atom to
their preferred error payload.