nhttp_h1 (nhttp_lib v1.1.1)
View SourceHTTP/1.1 request and response codec.
This module parses and encodes HTTP/1.1 messages (RFC 9112).
Parsing
Parsing functions return {ok, Result, BytesConsumed} where BytesConsumed
is the number of bytes consumed from the input. Use split_at/2 to get
the remaining buffer:
{ok, Request, Consumed} = nhttp_h1:parse_request(Binary),
Rest = nhttp_h1:split_at(Binary, Consumed).No remainder binary is built until the caller asks for one with
split_at/2.
For incomplete data, parsing returns {more, MinBytes} where MinBytes
is a hint for how many more bytes might be needed.
Options
The opts() map supports the following limits:
max_header_size- Maximum total size of all headers in bytes (default: infinity)max_headers_count- Maximum number of headers (default: infinity)max_body_size- Maximum body size in bytes (default: infinity)
When a limit is exceeded, parsing returns {error, header_too_large},
{error, too_many_headers}, or {error, {body_too_large, Size, Max}}
respectively.
With max_header_size set, an incomplete request head is also rejected
as header_too_large once the buffered input exceeds max_header_size
plus an 8 KiB request-line allowance: a head that never terminates (for
example a header line with no CRLF) cannot grow the caller's buffer
without bound. The same budget bounds chunked trailer sections, and
chunk-size lines longer than 1 KiB are rejected as
invalid_chunk_size.
Opts = #{max_header_size => 8192, max_headers_count => 100, max_body_size => 1048576},
case nhttp_h1:parse_request(Binary, Opts) of
{ok, Request, Consumed} -> handle_request(Request);
{error, header_too_large} -> respond_413();
{error, too_many_headers} -> respond_431();
{error, {body_too_large, _Size, _Max}} -> respond_413()
end.Encoding
A caller that sends the same field lines on many messages validates them
once with prepare_headers/1 and passes the result through enc_opts/0:
{ok, Static} = nhttp_h1:prepare_headers(StaticFields),
{ok, Io} = nhttp_h1:encode_response(Resp, #{prepared => Static}).A prepared block is validated once, the field lines of each message are
validated again, and no octet reaches the output unread. The derived
Content-Length comes first, then the prepared block, then the field lines
of this message.
A caller that holds no block to reuse calls the encoder directly:
{ok, IOList} = nhttp_h1:encode_request(Request).
{ok, IOList} = nhttp_h1:encode_response(Response).The encoders validate what they serialise and return
{error, t:encode_error/0} for a field name that is not a token, a field
value or reason phrase that carries CR, LF, NUL, or another control byte,
and a request target that carries a byte at or below 0x20. RFC 9112
Section 11.1 names that filter as the mitigation for response splitting and
request smuggling. A refused message is never repaired and never truncated.
encode_request/1 and encode_response/1 consume the canonical
nhttp_lib:request/0 / nhttp_lib:response/0 map shape. The body
field is for the convenience case where the whole payload fits in
memory: it is emitted inline after the header block and a
Content-Length is derived if neither Content-Length nor
Transfer-Encoding is present in headers.
For streaming bodies, do not populate body in the map. Send the
header block first via encode_response_head/3, then emit each chunk
via encode_chunk/1, then close the body with encode_last_chunk/0
(set Transfer-Encoding: chunked in the headers). A message that carries
a trailer section closes with encode_trailers/1 in place of
encode_last_chunk/0. The same staged pattern applies to chunked requests.
Summary
Types
Encoder options for encode_response/2.
Reason an encoder refuses to serialise a message.
A field block that prepare_headers/1 validated once.
Functions
Compute the response body framing mode from the request method, response
status, and response headers (RFC 9112 §6.3).
Pure helper: pass the values parsed by parse_response_headers/1,2 plus
the method of the matching request. The returned body_stream() is fed
into parse_response_body/2.
Framing rules in order
Encode a chunk for chunked transfer encoding.
Encode the final (zero-length) chunk.
Encode an HTTP/1.1 request to iolist.
The encoder refuses a message that cannot be framed unambiguously on the
wire. It returns {error, {invalid_request_target, Target}} for a target
that is empty or that carries a byte at or below 0x20 or the byte 0x7F,
{error, {invalid_field_name, Name}} for a field name that is not a token
(RFC 9110 Section 5.6.2), and {error, {invalid_field_value, Value}} for a
field value that carries CR, LF, NUL, another control byte, or 0x7F
(RFC 9110 Section 5.5).
RFC 9112 Section 2.2 forbids a sender to generate a bare CR in any protocol
element other than the content, and Section 11.1 names this filtering as the
mitigation for request smuggling and response splitting. No value is
repaired and no byte is stripped. The message is refused whole.
Encode an HTTP/1.1 request to iolist under the given encoder options.
See encode_request/1 for the framing rules and the rejected byte classes.
The request path reads the prepared key of enc_opts/0 only. A request
carries a derived Content-Length when its body is not empty, so
content_length has no meaning here.
Encode an HTTP/1.1 response to iolist.
Equivalent to encode_response(Resp, #{}). The encoder adds
Content-Length when the header list carries neither content-length nor
transfer-encoding, including a Content-Length: 0 on an empty body.
The encoder adds no Content-Length at a 1xx, 204, or 304 status. RFC 9110
Section 8.6 forbids the field at 1xx and 204. It permits the field at 304
only at the length that a 200 response would have carried, which this
encoder cannot compute, so a caller that knows the value supplies it in the
header list.
A 2xx response to a CONNECT request also carries no Content-Length. The
response map holds no request method, so that case needs
encode_response/2 with #{content_length => omit}.
The encoder refuses a message that cannot be framed unambiguously on the
wire. It returns {error, {invalid_reason_phrase, Reason}} for a reason
phrase outside 1*( HTAB / SP / VCHAR / obs-text ) (RFC 9112 Section 4.1),
{error, {invalid_field_name, Name}} for a field name that is not a token
(RFC 9110 Section 5.6.2), and {error, {invalid_field_value, Value}} for a
field value that carries CR, LF, NUL, another control byte, or 0x7F
(RFC 9110 Section 5.5). An empty reason phrase stays legal, because the
status-line grammar makes the element optional.
RFC 9112 Section 2.2 forbids a sender to generate a bare CR in any protocol
element other than the content, and Section 11.1 names this filtering as the
mitigation for response splitting. No value is repaired and no byte is
stripped. The message is refused whole.
Encode an HTTP/1.1 response to iolist under the given encoder options.
See encode_response/1 for the framing rules, the rejected byte classes,
and enc_opts/0 for the options.
A trailers key that holds a non-empty field list frames the body with the
chunked transfer coding: the header block, one chunk that carries the whole
body, then the last chunk, the trailer section and the closing CRLF. The
header list must already carry a Transfer-Encoding whose final coding is
chunked, because RFC 9110 Section 6.5.1 makes a trailer section possible
only when an explicit framing mechanism enables it, and RFC 9112
Section 7.1.2 names the chunked transfer coding as that mechanism for
HTTP/1.1. A response framed any other way holds no position for the field
lines, so the encoder returns
{error, {trailers_require_chunked, Trailers}} rather than drop them.
encode_trailers/1 states which trailer field names the encoder refuses.
A prepared block travels ahead of the field lines of this message. A
response that carries a trailer section keeps its Transfer-Encoding in the
message header list, because require_chunked_framing/2 reads that list and
a prepared block records only that a framing field is present, never which
transfer coding is final.
Encode HTTP/1.x response headers for streaming.
Used when sending chunked responses - sends status line + headers only. The
reason phrase comes from the status code, so only the header list is subject
to validation. See encode_response/1 for the rejected byte classes.
Encode HTTP/1.x response headers for streaming, under the given encoder
options.
The head path reads the prepared key of enc_opts/0 only. A streaming
response derives no Content-Length, so content_length has no meaning
here. The prepared block travels ahead of the field lines of this message.
Encode the terminating sequence of a chunked message, with a trailer section.
The return carries the last chunk, the trailer field lines and the single
CRLF that ends chunked-body (RFC 9112 Section 7.1: chunked-body = *chunk last-chunk trailer-section CRLF). A caller emits it in place of
encode_last_chunk/0, and encode_trailers([]) writes exactly what
encode_last_chunk/0 writes.
A trailer field is validated as a header field is. The encoder returns
{error, {invalid_field_name, Name}} for a name that is not a token
(RFC 9110 Section 5.6.2) and {error, {invalid_field_value, Value}} for a
value that carries CR, LF, NUL, another control byte, or 0x7F
(RFC 9110 Section 5.5).
The encoder also refuses the names transfer-encoding, content-length,
host and trailer with {error, {forbidden_trailer_field, Name}}. RFC 9112
Section 7.1.3 has a recipient compute the content length and rewrite
Transfer-Encoding at the point where the trailer section arrives, so a
recipient that merges one of those fields into the header section holds two
contradictory framing statements for one message. RFC 9112 Section 11.1 names
that filtering as the mitigation for request smuggling and response
splitting. The four names are a defence against a recipient that merges in
breach of RFC 9110 Section 6.5.1. They are not an RFC enumeration, and the
list does not grow.
RFC 9110 Section 6.5.1 puts the general rule on the sender: generate a
trailer field only when the definition of that field name permits trailer
use. No registry records that permission, so the encoder cannot check it and
the caller owns it. RFC 9110 Section 6.6.2 asks a sender that intends to
write a trailer section to announce the names in a Trailer header field.
Signal end-of-stream for a response body parse driven by
parse_response_body/2.
Used to terminate until_close framing when the underlying transport
closes, and to surface mid-body framing errors for {length, _} and
{chunked, _}.
Parse an HTTP/1.1 request from binary. Returns {ok, Request, BytesConsumed} on success. Use split_at/2 to get the remaining buffer.
Parse HTTP/1.1 request with options. Options can include: max_header_size, max_headers_count, max_body_size.
Feed body bytes for a streaming request whose headers were parsed via
parse_request_headers/1,2.
Returns one of
Parse HTTP/1.1 request headers only, without consuming the body, enforcing
the supplied limits. Returns {ok, Request, BodyStream, BytesConsumed}.
The body framing mode is encoded in BodyStream
Parse an HTTP/1.1 response from binary. Returns {ok, Response, BytesConsumed} on success. Use split_at/2 to get the remaining buffer.
Parse HTTP/1.1 response with options. Options can include: max_header_size, max_headers_count, max_body_size.
Feed body bytes for a streaming response whose headers were parsed via
parse_response_headers/1,2 and whose framing mode was selected via
body_stream_from_response/3.
For none and {length, 0}, returns {ok, [{fin, []}], none, 0}.
For {length, N>0} and {chunked, _}, behaves identically to
parse_request_body/2.
For until_close, emits [{data, _}] for whatever bytes are in the
buffer and keeps the stream open. The caller must signal EOF via
finalize_response_body/1 when the underlying transport closes.
Equivalent to parse_response_head/2.
Parse HTTP/1.1 response headers only, like parse_response_headers/2, but
also return the reason phrase and protocol version from the status line.
Returns {ok, Status, Reason, Version, Headers, Rest} where Rest is the
binary after the headers. Used by streaming callers that must preserve the
full response head (status, reason, version) while reading the body
separately via parse_response_body/2.
Parse HTTP/1.1 response headers only, without body, enforcing the supplied
limits (max_header_size, max_headers_count).
Validate a field list once and return the field lines as one binary.
The encoders read every octet of every field name and every field value on
every call, because RFC 9112 Section 11.1 names that filtering as the
mitigation for request smuggling and response splitting. A caller that sends
the same field lines on many messages pays for the same octets on every
message. prepare_headers/1 moves that cost to one call.
Split buffer at position, returning the remainder.
Types
-type body_chunk() :: {data, binary()} | {fin, nhttp_lib:headers()} | {abort, nhttp_lib:error()}.
-type body_mode() :: undefined | {content_length, non_neg_integer()} | chunked.
-type body_stream() :: {chunked, chunked_st()} | {length, non_neg_integer()} | until_close | none.
-opaque chunked_st()
-type enc_opts() :: #{content_length => auto | omit, prepared => prepared()}.
Encoder options for encode_response/2.
content_length selects how the encoder frames a response:
auto(the default) addsContent-Lengthwhen the header list carries neithercontent-lengthnortransfer-encoding, and the status permits the field.omitsuppresses the automatic field at any status. A server that answers aCONNECTrequest with a 2xx status uses it, because RFC 9110 Section 8.6 forbids the field there and the response map carries no request method.
prepared carries a field block that prepare_headers/1 validated once.
The encoder writes those octets without a second scan. The derived
Content-Length comes first, then the prepared block, then the field lines
of this message. content_length does not apply to a request, because the
derived field there follows the body length.
-type encode_error() :: {invalid_field_name, binary()} | {invalid_field_value, binary()} | {invalid_reason_phrase, binary()} | {invalid_request_target, binary()} | {forbidden_trailer_field, binary()} | {trailers_require_chunked, nhttp_lib:headers()}.
Reason an encoder refuses to serialise a message.
Each arm carries the offending value so that the caller can log it. The encoder never repairs the value and never strips a byte from it.
-type opts() :: #{max_header_size => pos_integer(), max_headers_count => pos_integer(), max_body_size => pos_integer(), scheme => nhttp_lib:scheme(), peer => nhttp_lib:peer()}.
-type parse_error() :: bad_request_line | bad_status_line | bad_header | header_too_large | too_many_headers | {body_too_large, Size :: non_neg_integer(), Max :: non_neg_integer()} | invalid_content_length | duplicate_content_length | conflicting_framing | unsupported_transfer_encoding | invalid_chunk_size | incomplete_chunk | invalid_method | invalid_version | unexpected_eof | {protocol_error, term()}.
-type parse_result(T) :: {ok, T, BytesConsumed :: pos_integer()} | {more, MinBytes :: pos_integer()} | {error, parse_error()}.
-opaque prepared()
A field block that prepare_headers/1 validated once.
The value holds the field lines as one binary and the answer to the framing
question: whether one of those lines is Content-Length or
Transfer-Encoding.
-type req() :: nhttp_lib:request().
-type resp() :: nhttp_lib:response().
-type version() :: http1_0 | http1_1.
Functions
-spec body_stream_from_response(nhttp_lib:method(), nhttp_lib:status(), nhttp_lib:headers()) -> body_stream().
Compute the response body framing mode from the request method, response
status, and response headers (RFC 9112 §6.3).
Pure helper: pass the values parsed by parse_response_headers/1,2 plus
the method of the matching request. The returned body_stream() is fed
into parse_response_body/2.
Framing rules in order:
HEADrequest →none(HEAD responses never have a body).- 1xx, 204, 304 status →
none. Transfer-Encodingwithchunkedas the single final coding →{chunked, _}. EveryTransfer-Encodingfield line contributes to the coding list, in order of receipt (RFC 9110 §5.3).Transfer-Encodingwith any other coding list →until_close(RFC 9112 §6.3 #4).Content-Length: N→{length, N}.- otherwise →
until_close(RFC 9112 §6.3 #7). The chunked / length walkers do not enforce header or body size limits in this entry point. Callers reading from untrusted peers should validate sizes at the recv site or wrap the stream.
Encode a chunk for chunked transfer encoding.
-spec encode_last_chunk() -> binary().
Encode the final (zero-length) chunk.
-spec encode_request(req()) -> {ok, iolist()} | {error, encode_error()}.
Encode an HTTP/1.1 request to iolist.
The encoder refuses a message that cannot be framed unambiguously on the
wire. It returns {error, {invalid_request_target, Target}} for a target
that is empty or that carries a byte at or below 0x20 or the byte 0x7F,
{error, {invalid_field_name, Name}} for a field name that is not a token
(RFC 9110 Section 5.6.2), and {error, {invalid_field_value, Value}} for a
field value that carries CR, LF, NUL, another control byte, or 0x7F
(RFC 9110 Section 5.5).
RFC 9112 Section 2.2 forbids a sender to generate a bare CR in any protocol
element other than the content, and Section 11.1 names this filtering as the
mitigation for request smuggling and response splitting. No value is
repaired and no byte is stripped. The message is refused whole.
-spec encode_request(req(), enc_opts()) -> {ok, iolist()} | {error, encode_error()}.
Encode an HTTP/1.1 request to iolist under the given encoder options.
See encode_request/1 for the framing rules and the rejected byte classes.
The request path reads the prepared key of enc_opts/0 only. A request
carries a derived Content-Length when its body is not empty, so
content_length has no meaning here.
{ok, Static} = nhttp_h1:prepare_headers([{<<"User-Agent">>, <<"acme/1.0">>}]),
{ok, Io} = nhttp_h1:encode_request(Req, #{prepared => Static}).
-spec encode_response(resp()) -> {ok, iolist()} | {error, encode_error()}.
Encode an HTTP/1.1 response to iolist.
Equivalent to encode_response(Resp, #{}). The encoder adds
Content-Length when the header list carries neither content-length nor
transfer-encoding, including a Content-Length: 0 on an empty body.
The encoder adds no Content-Length at a 1xx, 204, or 304 status. RFC 9110
Section 8.6 forbids the field at 1xx and 204. It permits the field at 304
only at the length that a 200 response would have carried, which this
encoder cannot compute, so a caller that knows the value supplies it in the
header list.
A 2xx response to a CONNECT request also carries no Content-Length. The
response map holds no request method, so that case needs
encode_response/2 with #{content_length => omit}.
The encoder refuses a message that cannot be framed unambiguously on the
wire. It returns {error, {invalid_reason_phrase, Reason}} for a reason
phrase outside 1*( HTAB / SP / VCHAR / obs-text ) (RFC 9112 Section 4.1),
{error, {invalid_field_name, Name}} for a field name that is not a token
(RFC 9110 Section 5.6.2), and {error, {invalid_field_value, Value}} for a
field value that carries CR, LF, NUL, another control byte, or 0x7F
(RFC 9110 Section 5.5). An empty reason phrase stays legal, because the
status-line grammar makes the element optional.
RFC 9112 Section 2.2 forbids a sender to generate a bare CR in any protocol
element other than the content, and Section 11.1 names this filtering as the
mitigation for response splitting. No value is repaired and no byte is
stripped. The message is refused whole.
-spec encode_response(resp(), enc_opts()) -> {ok, iolist()} | {error, encode_error()}.
Encode an HTTP/1.1 response to iolist under the given encoder options.
See encode_response/1 for the framing rules, the rejected byte classes,
and enc_opts/0 for the options.
A trailers key that holds a non-empty field list frames the body with the
chunked transfer coding: the header block, one chunk that carries the whole
body, then the last chunk, the trailer section and the closing CRLF. The
header list must already carry a Transfer-Encoding whose final coding is
chunked, because RFC 9110 Section 6.5.1 makes a trailer section possible
only when an explicit framing mechanism enables it, and RFC 9112
Section 7.1.2 names the chunked transfer coding as that mechanism for
HTTP/1.1. A response framed any other way holds no position for the field
lines, so the encoder returns
{error, {trailers_require_chunked, Trailers}} rather than drop them.
encode_trailers/1 states which trailer field names the encoder refuses.
A prepared block travels ahead of the field lines of this message. A
response that carries a trailer section keeps its Transfer-Encoding in the
message header list, because require_chunked_framing/2 reads that list and
a prepared block records only that a framing field is present, never which
transfer coding is final.
-spec encode_response_head(version(), nhttp_lib:status(), nhttp_lib:headers()) -> {ok, iolist()} | {error, encode_error()}.
Encode HTTP/1.x response headers for streaming.
Used when sending chunked responses - sends status line + headers only. The
reason phrase comes from the status code, so only the header list is subject
to validation. See encode_response/1 for the rejected byte classes.
-spec encode_response_head(version(), nhttp_lib:status(), nhttp_lib:headers(), enc_opts()) -> {ok, iolist()} | {error, encode_error()}.
Encode HTTP/1.x response headers for streaming, under the given encoder
options.
The head path reads the prepared key of enc_opts/0 only. A streaming
response derives no Content-Length, so content_length has no meaning
here. The prepared block travels ahead of the field lines of this message.
-spec encode_trailers(nhttp_lib:headers()) -> {ok, iolist()} | {error, encode_error()}.
Encode the terminating sequence of a chunked message, with a trailer section.
The return carries the last chunk, the trailer field lines and the single
CRLF that ends chunked-body (RFC 9112 Section 7.1: chunked-body = *chunk last-chunk trailer-section CRLF). A caller emits it in place of
encode_last_chunk/0, and encode_trailers([]) writes exactly what
encode_last_chunk/0 writes.
A trailer field is validated as a header field is. The encoder returns
{error, {invalid_field_name, Name}} for a name that is not a token
(RFC 9110 Section 5.6.2) and {error, {invalid_field_value, Value}} for a
value that carries CR, LF, NUL, another control byte, or 0x7F
(RFC 9110 Section 5.5).
The encoder also refuses the names transfer-encoding, content-length,
host and trailer with {error, {forbidden_trailer_field, Name}}. RFC 9112
Section 7.1.3 has a recipient compute the content length and rewrite
Transfer-Encoding at the point where the trailer section arrives, so a
recipient that merges one of those fields into the header section holds two
contradictory framing statements for one message. RFC 9112 Section 11.1 names
that filtering as the mitigation for request smuggling and response
splitting. The four names are a defence against a recipient that merges in
breach of RFC 9110 Section 6.5.1. They are not an RFC enumeration, and the
list does not grow.
RFC 9110 Section 6.5.1 puts the general rule on the sender: generate a
trailer field only when the definition of that field name permits trailer
use. No registry records that permission, so the encoder cannot check it and
the caller owns it. RFC 9110 Section 6.6.2 asks a sender that intends to
write a trailer section to announce the names in a Trailer header field.
-spec finalize_response_body(body_stream()) -> {ok, [body_chunk()]} | {error, parse_error()}.
Signal end-of-stream for a response body parse driven by
parse_response_body/2.
Used to terminate until_close framing when the underlying transport
closes, and to surface mid-body framing errors for {length, _} and
{chunked, _}.
none,{length, 0}, oruntil_close→{ok, [{fin, []}]}.{length, N>0}→{error, unexpected_eof}.{chunked, _}mid-body →{error, unexpected_eof}.
-spec parse_request(binary()) -> parse_result(req()).
Parse an HTTP/1.1 request from binary. Returns {ok, Request, BytesConsumed} on success. Use split_at/2 to get the remaining buffer.
-spec parse_request(binary(), opts()) -> parse_result(req()).
Parse HTTP/1.1 request with options. Options can include: max_header_size, max_headers_count, max_body_size.
-spec parse_request_body(binary(), body_stream()) -> {ok, [body_chunk()], body_stream(), non_neg_integer()} | {more, pos_integer(), body_stream()} | {error, parse_error()}.
Feed body bytes for a streaming request whose headers were parsed via
parse_request_headers/1,2.
Returns one of:
{ok, Chunks, NewStream, BytesConsumed}: emits zero or morebody_chunk()events. A{fin, Trailers}chunk signals the body is fully consumed; subsequent calls on the returned stream are not needed.{more, MinBytes, NewStream}: not enough buffer to make progress. The caller should buffer at leastMinBytesmore bytes and call again with the sameNewStream.{error, parse_error()}: framing error (bad chunk size, body too large, header limits exceeded in trailers).
-spec parse_request_headers(binary()) -> {ok, req(), body_stream(), non_neg_integer()} | {more, pos_integer()} | {error, parse_error()}.
Warning
This zero-arg variant enforces no size or count limits on the input
and is unsafe to use against untrusted peers. Production callers reading
from the network MUST use parse_request_headers/2 and pass
max_header_size, max_headers_count, and max_body_size (see
opts/0).
Parse HTTP/1.1 request headers only, without consuming the body. Returns
{ok, Request, BodyStream, BytesConsumed}. The returned request map has
body => streaming; the body bytes (if any) are read separately via
parse_request_body/2.
-spec parse_request_headers(binary(), opts()) -> {ok, req(), body_stream(), non_neg_integer()} | {more, pos_integer()} | {error, parse_error()}.
Parse HTTP/1.1 request headers only, without consuming the body, enforcing
the supplied limits. Returns {ok, Request, BodyStream, BytesConsumed}.
The body framing mode is encoded in BodyStream:
none: no body (noContent-Length, noTransfer-Encoding, orContent-Length: 0).{length, N}:Nbody bytes remain to be read.{chunked, _}: chunked transfer encoding; opaque state to feed back intoparse_request_body/2. The returned request map carriesbody => streaminginstead of buffered bytes. Useparse_request_body/2to drive the body stream. EveryTransfer-Encodingfield line contributes to one coding list, in order of receipt (RFC 9110 §5.3). The parser returns{error, unsupported_transfer_encoding}unless that list holdschunkedexactly once, as the final coding (RFC 9112 §6.1 and §6.3 #4).
-spec parse_response(binary()) -> parse_result(resp()).
Parse an HTTP/1.1 response from binary. Returns {ok, Response, BytesConsumed} on success. Use split_at/2 to get the remaining buffer.
-spec parse_response(binary(), opts()) -> parse_result(resp()).
Parse HTTP/1.1 response with options. Options can include: max_header_size, max_headers_count, max_body_size.
-spec parse_response_body(binary(), body_stream()) -> {ok, [body_chunk()], body_stream(), non_neg_integer()} | {more, pos_integer(), body_stream()} | {error, parse_error()}.
Feed body bytes for a streaming response whose headers were parsed via
parse_response_headers/1,2 and whose framing mode was selected via
body_stream_from_response/3.
For none and {length, 0}, returns {ok, [{fin, []}], none, 0}.
For {length, N>0} and {chunked, _}, behaves identically to
parse_request_body/2.
For until_close, emits [{data, _}] for whatever bytes are in the
buffer and keeps the stream open. The caller must signal EOF via
finalize_response_body/1 when the underlying transport closes.
-spec parse_response_head(binary()) -> {ok, nhttp_lib:status(), binary(), version(), nhttp_lib:headers(), binary()} | {more, pos_integer()} | {error, parse_error()}.
Equivalent to parse_response_head/2.
-spec parse_response_head(binary(), opts()) -> {ok, nhttp_lib:status(), binary(), version(), nhttp_lib:headers(), binary()} | {more, pos_integer()} | {error, parse_error()}.
Parse HTTP/1.1 response headers only, like parse_response_headers/2, but
also return the reason phrase and protocol version from the status line.
Returns {ok, Status, Reason, Version, Headers, Rest} where Rest is the
binary after the headers. Used by streaming callers that must preserve the
full response head (status, reason, version) while reading the body
separately via parse_response_body/2.
-spec parse_response_headers(binary()) -> {ok, nhttp_lib:status(), nhttp_lib:headers(), binary()} | {more, pos_integer()} | {error, parse_error()}.
Warning
This zero-arg variant enforces no size or count limits on the input
and is unsafe to use against untrusted peers. A malicious response can
exhaust memory by sending arbitrarily many or arbitrarily large header
fields. Production callers reading from the network MUST use
parse_response_headers/2 and pass max_header_size and
max_headers_count (see opts/0).
Parse HTTP/1.1 response headers only, without body. Returns
{ok, Status, Headers, Rest} where Rest is the binary after headers.
Used for streaming responses where the body is read separately.
-spec parse_response_headers(binary(), opts()) -> {ok, nhttp_lib:status(), nhttp_lib:headers(), binary()} | {more, pos_integer()} | {error, parse_error()}.
Parse HTTP/1.1 response headers only, without body, enforcing the supplied
limits (max_header_size, max_headers_count).
-spec prepare_headers(nhttp_lib:headers()) -> {ok, prepared()} | {error, encode_error()}.
Validate a field list once and return the field lines as one binary.
The encoders read every octet of every field name and every field value on
every call, because RFC 9112 Section 11.1 names that filtering as the
mitigation for request smuggling and response splitting. A caller that sends
the same field lines on many messages pays for the same octets on every
message. prepare_headers/1 moves that cost to one call.
{ok, Static} = nhttp_h1:prepare_headers([
{<<"Server">>, <<"acme/1.0">>},
{<<"Cache-Control">>, <<"no-store">>}
]),
{ok, Io} = nhttp_h1:encode_response(Resp, #{prepared => Static}).The value is opaque and this function is the only path that builds one. A
caller that fabricates the record writes octets that no validator read, and
the library cannot prevent that any more than it can prevent a call to a
private function.
prepare_headers/1 refuses exactly what the encoders refuse, with the same
values: {error, {invalid_field_name, Name}} for a name that is not a token
(RFC 9110 Section 5.6.2) and {error, {invalid_field_value, Value}} for a
value that carries CR, LF, NUL, another control byte, or 0x7F
(RFC 9110 Section 5.5).
The block is for field lines that many messages reuse.
iolist_to_binary/1 copies them once here, so a caller that prepares a
block for a single message pays more than a caller that prepares nothing.
The encoder does not compare a caller supplied Content-Length against the
body length, and a prepared block does not change that.
-spec split_at(binary(), non_neg_integer()) -> binary().
Split buffer at position, returning the remainder.