nhttp_hpack (nhttp_lib v1.1.1)
View SourceHPACK header compression for HTTP/2 (RFC 7541).
This module implements the HPACK header compression format used by HTTP/2. It provides stateful encoding and decoding of header fields using static and dynamic tables.
Usage
{ok, EncState0} = nhttp_hpack:new(),
{ok, DecState0} = nhttp_hpack:new(),
Headers = [{<<":method">>, <<"GET">>}, {<<":path">>, <<"/">>}],
{ok, HeaderBlock, EncState1} = nhttp_hpack:encode(Headers, EncState0),
{ok, DecodedHeaders, DecState1} = nhttp_hpack:decode(HeaderBlock, DecState0).Field names on encode
encode/2 and encode/3 write every field name in lowercase. RFC 9113 §8.2
requires that a field name is converted to lowercase when an HTTP/2 message is
constructed, and RFC 9113 §8.2.1 makes an uppercase name on the wire malformed.
The conversion is silent: the return type does not change, and the caller reads
no report of it.
A name that already holds no octet in 0x41-0x5A costs one scan and no
allocation. The lowercase name is what the static table lookup reads, what the
literal representation writes and what the dynamic table holds, so the octets in
the table and the octets on the wire agree.
Field validity on decode
Every field name and every field value that arrives as a literal is read
against the minimal rule of RFC 9113 §8.2.1. A block that carries such a
field returns {invalid_field, Reason, NewState}, which is apart from the
{error, Reason} of an HPACK failure: RFC 9113 §8.1.1 makes a malformed
field a stream error, where RFC 9113 §4.3 makes a decode failure a
connection error.
The block runs to its end either way. RFC 9113 §4.3 makes an endpoint
decompress a field block even when it discards the frames, so NewState
carries every dynamic table update that the block asks for. A decoder that
skips the update of a refused field falls out of step with the peer encoder,
and every later block on that connection then decodes to the wrong field.
An entry therefore holds the verdict on its field, read once at insert. A field that arrives by index carries that verdict out again and needs no second read of the octets. The verdict names the part that failed, so a field that reuses the name by index drops a verdict against the value and reads a fresh value from the wire.
Summary
Types
The outcome of a header block decode.
A field that breaks the minimal rule of RFC 9113 §8.2.1.
Functions
Decode a header block.
See decode/3 for the invalid_field return.
Decode a header block, aborting with {error, header_list_too_large} once
the cumulative decoded list size exceeds max_list_size. The check matches
the RFC 9113 §10.5.1 octet count (name + value + 32 per entry).
Encode headers without Huffman encoding.
Encode headers with options.
Check if the dynamic table is empty.
Create a new HPACK state with default max size (4096 bytes).
Create a new HPACK state with specified max size.
Update the maximum table size (from SETTINGS_HEADER_TABLE_SIZE). Immediately evicts entries if the new size is smaller than current table size.
Get the current dynamic table size in bytes.
Types
-type decode_error() :: dynamic_table_size_exceeded | invalid_table_index | integer_overflow | invalid_huffman | incomplete_header_block | header_list_too_large | field_error().
-type decode_opts() :: #{max_list_size => pos_integer() | infinity}.
-type decode_result() :: {ok, headers(), state()} | {invalid_field, field_error(), state()} | {error, decode_error()}.
The outcome of a header block decode.
{invalid_field, Reason, NewState} reports a field that breaks RFC 9113
§8.2.1. The block decompressed, and NewState carries every dynamic table
update that the block asks for, because RFC 9113 §4.3 makes an endpoint
decompress a field block even when it discards the frames. The caller keeps
NewState and treats the message as malformed, a stream error of type
PROTOCOL_ERROR (RFC 9113 §8.1.1).
{error, Reason} reports a decode failure. The block did not decompress,
the state is unusable, and RFC 9113 §4.3 makes this a connection error of
type COMPRESSION_ERROR.
-type encode_opts() :: #{huffman => boolean()}.
-type field_error() :: uppercase_header_name | invalid_header_name | invalid_header_value.
A field that breaks the minimal rule of RFC 9113 §8.2.1.
uppercase_header_name names the case that the RFC lists apart from the
other invalid name characters.
-opaque state()
Functions
-spec decode(Data :: binary(), State :: state()) -> decode_result().
Decode a header block.
See decode/3 for the invalid_field return.
-spec decode(Data :: binary(), State :: state(), Opts :: decode_opts()) -> decode_result().
Decode a header block, aborting with {error, header_list_too_large} once
the cumulative decoded list size exceeds max_list_size. The check matches
the RFC 9113 §10.5.1 octet count (name + value + 32 per entry).
Encode headers without Huffman encoding.
-spec encode(Headers :: headers(), State :: state(), Opts :: encode_opts()) -> {ok, iodata(), state()}.
Encode headers with options.
Check if the dynamic table is empty.
-spec new() -> {ok, state()}.
Create a new HPACK state with default max size (4096 bytes).
-spec new(MaxSize :: non_neg_integer()) -> {ok, state()}.
Create a new HPACK state with specified max size.
-spec set_max_table_size(MaxSize :: non_neg_integer(), State :: state()) -> {ok, state()}.
Update the maximum table size (from SETTINGS_HEADER_TABLE_SIZE). Immediately evicts entries if the new size is smaller than current table size.
-spec table_size(State :: state()) -> non_neg_integer().
Get the current dynamic table size in bytes.