Fetch.Parser (Fetch v0.1.0)
View SourcePure functions that parse an HTTP/1.1 response head and decide how the body is framed.
A response on the wire looks like this:
HTTP/1.1 200 OK\r\n <- status line
Content-Type: text/plain\r\n <- header lines
Content-Length: 5\r\n
\r\n <- empty line ends the head
hello <- bodyThe head passed to parse_head/1 is everything before the first
\r\n\r\n. How many body bytes follow is decided by body_framing/3.
The parser is strict where leniency is dangerous (header syntax, conflicting Content-Length) and tolerant only where it is harmless (a missing reason phrase).
Summary
Functions
Decides how the response body is delimited (RFC 9112 §6.3).
Returns true if value can be a header value: it must not contain CR, LF or
NUL (RFC 9110 §5.5). Those bytes are how header injection works.
Returns true if the connection may carry another request after a response with this version and headers (RFC 9112 §9.3).
Parses the size line at the start of a chunk (RFC 9112 §7.1)
Parses a response head: a status line followed by header lines, separated by CRLF.
Parses one header line: field-name ":" OWS field-value OWS (RFC 9112 §5).
Parses header lines into {lowercase_name, value} tuples, keeping order
and duplicates.
Parses HTTP/1.1 200 OK into %{version: "HTTP/1.1", status: 200, reason: "OK"}.
Parses the trailer section after the last chunk: header lines followed by an empty line. Usually there are no trailers and the section is just CRLF.
Splits a buffer at the end of the head.
Returns true if name is an RFC 9110 token — the allowed syntax for header
names: one or more of !#$%&'*+-.^_`|~, digits and ASCII letters.
Types
Functions
Decides how the response body is delimited (RFC 9112 §6.3).
- responses to HEAD, and 1xx/204/304 responses have no body
transfer-encodingwins overcontent-length; only plainchunkedis supportedcontent-lengthgives the exact size- otherwise the body lasts until the server closes the connection
Returns true if value can be a header value: it must not contain CR, LF or
NUL (RFC 9110 §5.5). Those bytes are how header injection works.
Returns true if the connection may carry another request after a response with this version and headers (RFC 9112 §9.3).
HTTP/1.1 connections are persistent unless connection: close is sent.
HTTP/1.0 connections close by default; its old connection: keep-alive
extension is not supported.
@spec parse_chunk_size(binary()) :: {:ok, non_neg_integer(), binary()} | :more | {:error, {:parse, term()}}
Parses the size line at the start of a chunk (RFC 9112 §7.1):
chunk-size [ chunk-ext ] CRLF <- this line
chunk-data CRLFThe size is hexadecimal. Chunk extensions (;name=value) have no meaning
for us and are skipped. Returns the size and the bytes after the line.
A size of 0 marks the last chunk, followed by the trailer section.
Parses a response head: a status line followed by header lines, separated by CRLF.
Parses one header line: field-name ":" OWS field-value OWS (RFC 9112 §5).
- the name is case-insensitive, so it is lowercased
- whitespace between name and colon is invalid (RFC 9112 §5.1) — it was used for request smuggling
- a line starting with space/tab is obsolete line folding (RFC 9112 §5.2), rejected here
- optional whitespace (SP / HTAB) around the value is removed
Parses header lines into {lowercase_name, value} tuples, keeping order
and duplicates.
@spec parse_status_line(binary()) :: {:ok, status_line()} | {:error, {:parse, term()}}
Parses HTTP/1.1 200 OK into %{version: "HTTP/1.1", status: 200, reason: "OK"}.
RFC 9112 §4: HTTP-version SP 3DIGIT SP [reason-phrase]. The reason phrase
carries no meaning, so it may be empty; a missing space before it is
tolerated because real servers send HTTP/1.1 200.
Parses the trailer section after the last chunk: header lines followed by an empty line. Usually there are no trailers and the section is just CRLF.
Splits a buffer at the end of the head.
Returns {:ok, head, rest} where rest is the beginning of the body, or
:more when the terminating empty line has not arrived yet.
Returns true if name is an RFC 9110 token — the allowed syntax for header
names: one or more of !#$%&'*+-.^_`|~, digits and ASCII letters.