erlang_qpack

View Source

QPACK header compression for HTTP/3 (RFC 9204) in pure Erlang.

Features

  • Full RFC 9204 compliance
  • Static table with O(1) map-based lookups (99 entries)
  • Dynamic table with O(1) lookups and FIFO eviction
  • Encoder/decoder stream support for HTTP/3
  • Optional Huffman coding (RFC 7541 Appendix B)
  • Stateless and stateful APIs
  • Drop-in replacement for hackney_qpack/livery_qpack

Installation

Add to your rebar.config:

{deps, [
    {qpack, {git, "https://github.com/benoitc/erlang_qpack.git", {tag, "0.1.0"}}}
]}.

Quick Start

Stateless API (static table only)

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

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

Stateful API (with dynamic table)

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

%% Encode with state
{Encoded, State1} = qpack:encode(Headers, State0),

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

HTTP/3 Stream Integration

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

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

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

API Reference

See Usage Guide for detailed documentation.

Benchmarks

Performance on Apple M3 Pro (Erlang/OTP 28):

OperationTimeThroughput
Encode simple request (4 headers, static)0.45 us2.2M ops/s
Encode typical request (8 headers)1.25 us800K ops/s
Encode large (50 custom headers)7.23 us138K ops/s
Decode simple request (4 headers)0.14 us7.0M ops/s
Decode typical request (8 headers)0.43 us2.3M ops/s
Huffman encode (15 bytes)0.15 us6.5M ops/s
Huffman decode (15 bytes)0.15 us6.7M ops/s
Process encoder instruction0.07 us14.9M ops/s

Run benchmarks:

rebar3 eunit && erl -pa _build/test/lib/qpack/ebin -pa _build/test/lib/qpack/test \
  -noshell -eval "qpack_bench:run(), halt()."

Development

Clone with submodules for interop test data:

git clone --recurse-submodules https://github.com/benoitc/erlang_qpack.git

Or initialize submodules after cloning:

git submodule update --init --recursive

Run tests:

rebar3 eunit           # Unit tests
rebar3 ct              # Common Test (includes interop tests)

Interoperability Testing

The test/qifs submodule contains QPACK Interop Files (QIFs) from the qpackers/qifs repository. These are used to verify compatibility with other QPACK implementations.

The interop test suite (qpack_interop_SUITE) tests:

  • Decoding encoded data from other implementations (nghttp3, ls-qpack)
  • Round-trip encoding/decoding of QIF test files

Run interop tests specifically:

rebar3 ct --suite=qpack_interop_SUITE

QIF files are tab-separated header blocks. Encoded files use the format:

  • 8 bytes: stream ID (big-endian)
  • 4 bytes: block length (big-endian)
  • N bytes: encoded QPACK data

Build documentation:

rebar3 ex_doc

License

Apache License 2.0