Stateful HPACK header compression for HTTP/2 clients and servers (RFC 7541).
The codec operates on ordered lists of {name, value} binary pairs. It does
not own a connection, start processes, frame HTTP/2 messages, or validate HTTP
header semantics. The caller handles pseudo-header ordering, lowercase names,
SETTINGS exchange, and connection errors.
Keep one encoder for outgoing blocks and a separate decoder for incoming blocks on each connection, regardless of whether the application is a client or server. Retain the returned contexts across blocks and across streams, in the order blocks are sent or received. Do not share contexts between directions or connections, or change their struct fields directly.
encode_headers/2 and encode_headers/3 return {encoder, block} with a binary
block. decode/2 takes a complete binary block and returns
{:ok, decoder, headers} or {:error, reason}. Assemble HEADERS/PUSH_PROMISE
and CONTINUATION fragments before decoding; the codec has no incremental
input or partial-result interface. encode_headers/1 is a stateless literal
encoder returning iodata instead of a context.
Stateful example
The two contexts below represent opposite ends of one sending direction. Incremental indexing allows the second block to refer to the first block's dynamic table entry:
alias HTTP.HTTP2.HPACK
encoder = HPACK.new_encoder()
decoder = HPACK.new_decoder()
headers = [{"x-example", "one"}]
{encoder, first} = HPACK.encode_headers(encoder, headers, indexing: :incremental)
{:ok, decoder, ^headers} = HPACK.decode(decoder, first)
{_encoder, second} = HPACK.encode_headers(encoder, headers, indexing: :incremental)
{:ok, _decoder, ^headers} = HPACK.decode(decoder, second)
true = byte_size(second) < byte_size(first)Table capacity and SETTINGS
Both initial contexts use a 4096-byte dynamic table. Apply a received peer
SETTINGS_HEADER_TABLE_SIZE to the outgoing encoder with
set_max_dynamic_size/2. It evicts entries immediately and queues the required
size update at the beginning of the next encoded block, even an empty block.
Several changes before that block emit the smallest and final sizes as needed.
Apply your own advertised SETTINGS_HEADER_TABLE_SIZE to the incoming decoder
with acknowledge_max_dynamic_size/2 when the peer acknowledges it. The decoder
then enforces the acknowledged limit and any required leading shrink update.
The caller coordinates SETTINGS and ACK ordering; these functions do not send
frames. See new_decoder/1 for independent decoding resource limits.
Summary
Functions
Applies an acknowledged local SETTINGS_HEADER_TABLE_SIZE to a decoder.
Decodes one complete binary HPACK block using the incoming decoder context.
Encodes an ordered header list as stateless, uncompressed literal iodata.
Encodes an ordered header list with the default policies.
Encodes headers with explicit policies, returning {updated_encoder, binary_block}.
Encodes an HPACK integer with a 1-to-8-bit prefix and first-byte high bits.
Encodes a binary as an HPACK string literal without Huffman encoding.
Creates an incoming decoder with an empty dynamic table and resource limits.
Creates an outgoing encoder with an empty 4096-byte dynamic table.
Applies a dynamic table size limit and returns the updated context.
Types
@type t() :: %HTTP.HTTP2.HPACK{ dynamic: [header()], dynamic_size: non_neg_integer(), max_block_bytes: pos_integer(), max_decoded_bytes: pos_integer(), max_dynamic_size: non_neg_integer(), max_field_bytes: pos_integer(), max_fields: pos_integer(), pending_final_size: non_neg_integer() | nil, pending_min_size: non_neg_integer() | nil, required_min_size: non_neg_integer() | nil, required_size_update: boolean(), role: :encoder | :decoder, table_size: non_neg_integer() }
Functions
@spec acknowledge_max_dynamic_size(t(), non_neg_integer()) :: t()
Applies an acknowledged local SETTINGS_HEADER_TABLE_SIZE to a decoder.
Returns the updated decoder with the allowed maximum set to size, evicting
entries if its current capacity exceeds that maximum. A decrease below the
current capacity requires the next block to start with a table size update no
larger than the smallest required shrink. A later increase does not cancel
that requirement. Increasing the maximum alone does not increase the current
capacity; the peer chooses its capacity through a leading HPACK size update.
Accepts only a decoder context and a non-negative integer size. This
function neither sends a SETTINGS ACK nor applies an unacknowledged setting.
Decodes one complete binary HPACK block using the incoming decoder context.
Returns {:ok, updated_decoder, headers}, preserving field order and duplicate
names as {name, value} binary pairs, or {:error, reason}. Never-indexed
markers are not included in the returned pairs. No partial state or headers
are returned on error; do not retry a truncated fragment as a new block or
continue a connection with an unsynchronized compression context.
Limit failures are :hpack_block_too_large, :hpack_decoded_bytes_exceeded
(also used for the per-field limit), and :hpack_field_count_exceeded.
Malformed input can return :invalid_hpack_index,
:truncated_hpack_integer, :hpack_integer_too_large,
:truncated_hpack_string, :invalid_huffman_code, :invalid_huffman_eos,
:invalid_huffman_padding, :missing_dynamic_table_size_update, or
:invalid_dynamic_table_size_update. HTTP/2 connection-level error handling
and header semantic validation remain the caller's responsibility.
Encodes an ordered header list as stateless, uncompressed literal iodata.
Every {name, value} binary pair uses a literal without indexing and a literal
name; the result has type [iodata()]. Convert it with
IO.iodata_to_binary/1 before passing it to decode/2. This form does not use
or update a dynamic table, emit size updates, or mark fields as never indexed.
Use encode_headers/3 to select indexing, sensitivity, or Huffman policies.
Encodes an ordered header list with the default policies.
Takes an encoder context followed by a list of {name, value} binary pairs
and returns {updated_encoder, binary_block}. Equivalent to
encode_headers(encoder, headers, []): literal without indexing, no sensitive
names, and no Huffman encoding. Keep the updated encoder for subsequent blocks.
Encodes headers with explicit policies, returning {updated_encoder, binary_block}.
Arguments are an encoder context, an ordered list of {name, value} binary
pairs, and a keyword list or map with these options:
:indexing-:literal(default) emits literals without indexing;:incrementaluses matching static/dynamic entries or adds literals to the dynamic table;:neveremits literals marked never indexed.:sensitive- a list of header name binaries, default[]. Exact, case-sensitive name matches override:indexingwith:never. There is no built-in sensitive-name list or callback form.:huffman-:never(default) emits plain strings;:alwaysHuffman-encodes literal names and values, even when their encoding is larger.
Names and values are preserved without normalization. Pending table size
updates precede fields, including when headers is empty. The options select
policies for this call; retain the returned encoder's table state for the next
block. Supply the documented option values: the codec does not validate them
or return tagged encoding errors. Invalid argument shapes may raise.
@spec encode_integer(non_neg_integer(), 1..8, non_neg_integer()) :: binary()
Encodes an HPACK integer with a 1-to-8-bit prefix and first-byte high bits.
value is non-negative. high_bits must fit one byte and leave the prefix
bits clear. Returns a binary; most callers should use encode_headers/3.
Encodes a binary as an HPACK string literal without Huffman encoding.
Returns a binary containing the length prefix and the unchanged value bytes.
Creates an incoming decoder with an empty dynamic table and resource limits.
Options are a keyword list:
:max_dynamic_size- initial table capacity and allowed maximum in bytes; defaults to4096. Must be a non-negative integer. Table entries cost the name and value byte lengths plus 32 bytes each (RFC 7541).:max_block_bytes- maximum compressed bytes per complete block; defaults to65_536.:max_decoded_bytes- maximum sum of name and value bytes in a block, including indexed fields and Huffman expansion; defaults to65_536.:max_field_bytes- maximum name plus value bytes for one field; defaults to65_536.:max_fields- maximum number of fields in a block; defaults to256.
The four per-block limits must be positive integers. They do not include the
dynamic table's 32-byte entry overhead. Options are not validated by the
constructor; callers must supply values in these domains. There is no
:infinity option. Retain each successful decode/2 result for the next block.
Creates an outgoing encoder with an empty 4096-byte dynamic table.
Pass the returned context to encode_headers/2 or encode_headers/3, and keep
each returned encoder for the next block on the same connection direction.
@spec set_max_dynamic_size(t(), non_neg_integer()) :: t()
Applies a dynamic table size limit and returns the updated context.
For an encoder, size is the peer-advertised SETTINGS_HEADER_TABLE_SIZE.
It sets the encoder's capacity, evicts entries as needed, and queues leading
HPACK table size updates for its next encode_headers/2 or
encode_headers/3 call. An unchanged limit queues no update.
For a decoder, this is an alias for acknowledge_max_dynamic_size/2; call it
only at the corresponding local SETTINGS acknowledgement boundary. size
must be a non-negative integer, including zero to disable table storage.