Usage Guide

View Source

Overview

erlang_qpack provides QPACK header compression for HTTP/3 as specified in RFC 9204. It supports both stateless encoding (static table only) and stateful encoding with dynamic table management.

Stateless API

The simplest way to use qpack is with the stateless API, which only uses the static table:

%% Encode headers
Headers = [
    {<<":method">>, <<"GET">>},
    {<<":path">>, <<"/">>},
    {<<":scheme">>, <<"https">>},
    {<<":authority">>, <<"example.com">>}
],
Encoded = qpack:encode(Headers).

%% Decode headers
{ok, Decoded} = qpack:decode(Encoded).

Stateful API

For better compression, use the stateful API with a dynamic table:

%% Create state with 4KB dynamic table
State0 = qpack:new(#{max_dynamic_size => 4096}),

%% Encode headers - entries may be added to dynamic table
{Encoded, State1} = qpack:encode(Headers, State0),

%% Decode headers
{{ok, Decoded}, State2} = qpack:decode(Encoded, State1).

Stream Tracking

For HTTP/3 integration with section acknowledgment:

%% Encode with stream ID tracking
{Encoded, State1} = qpack:encode(Headers, StreamId, State0),

%% Get the Required Insert Count for this encoding
RIC = qpack:get_last_ric(State1).

Encoder Stream

The encoder generates instructions for the decoder's dynamic table:

%% Get pending encoder instructions to send to peer
Instructions = qpack:get_encoder_instructions(State),

%% Clear instructions after sending
State1 = qpack:clear_encoder_instructions(State).

Decoder Stream

Process instructions from the peer:

%% Process encoder instructions from peer (updates dynamic table)
{ok, State1} = qpack:process_encoder_instructions(Data, State0),

%% Handle incomplete data (streaming)
case qpack:process_encoder_instructions(Data, State0) of
    {ok, State1} ->
        %% All instructions processed
        State1;
    {incomplete, Remaining, State1} ->
        %% Buffer Remaining, wait for more data
        {Remaining, State1};
    {error, Reason} ->
        %% Protocol error
        error(Reason)
end.

Sending Acknowledgments

%% Acknowledge a decoded header section
Ack = qpack:encode_section_ack(StreamId),

%% Increment known received count
Increment = qpack:encode_insert_count_increment(Count).

Processing Acknowledgments

%% Process decoder instructions from peer
{ok, State1} = qpack:process_decoder_instructions(Data, State0).

Blocked Decoding

When a header block references dynamic table entries the decoder hasn't received yet:

case qpack:decode(Data, State) of
    {{ok, Headers}, State1} ->
        %% Successfully decoded
        {Headers, State1};
    {{blocked, RIC}, State1} ->
        %% Need to wait for encoder instructions up to RIC
        %% Buffer the header block and retry after processing more instructions
        {blocked, RIC, Data, State1};
    {{error, Reason}, State1} ->
        %% Decoding error
        error(Reason)
end.

Dynamic Table Management

%% Get current capacity
Capacity = qpack:get_dynamic_capacity(State),

%% Set new capacity (generates encoder instruction)
State1 = qpack:set_dynamic_capacity(NewCapacity, State),

%% Get insert count
InsertCount = qpack:get_insert_count(State),

%% Get known received count (acknowledged by peer)
KRC = qpack:get_known_received_count(State).

Huffman Encoding

The Huffman module can be used directly:

%% Encode
Encoded = qpack_huffman:encode(<<"www.example.com">>),

%% Decode
Decoded = qpack_huffman:decode(Encoded),

%% Decode with validation (RFC 7541 Section 5.2)
{ok, Decoded} = qpack_huffman:decode_safe(Encoded),

%% Check encoded size
Size = qpack_huffman:encoded_size(Data).

Error Handling

QPACK operations can return errors:

%% Decoding errors
{{error, incomplete}, State}           %% Truncated input
{{error, {invalid_static_index, N}}, State}
{{error, {invalid_dynamic_index, N}}, State}

%% Instruction processing errors
{error, {invalid_increment, N, InsertCount}}
{error, invalid_encoder_instruction}
{error, invalid_decoder_instruction}

Best Practices

  1. Reuse state - Create one state per connection and reuse it for all requests/responses
  2. Process instructions promptly - Process encoder/decoder stream data as it arrives
  3. Handle blocked decoding - Buffer blocked header blocks and retry after receiving more encoder instructions
  4. Size the dynamic table appropriately - 4KB-16KB is typical for most applications
  5. Send acknowledgments - Send section acknowledgments to allow the encoder to reference new entries