HPACK Implementation for Erlang
This implementation was designed for use by Chatterbox, but could be used by any HTTP/2 implementation (or, if something other than HTTP/2 has need for HPACK, this would work as well).
- Use in other projects
- A separate RFC seemed like a really clear abstraction
- Compression contexts (RFC-7541 Section 2.2)
- Dynamic table management (RFC-7541 Section 4)
- Primitive type representations (RFC-7541 Section 5)
- Binary format (RFC-7541 Section 6)
- Indexed Header Field Representation
- Literal Header Field Representation
- Dynamic Table Size Update
An HTTP/2 implementation should be responsible for anything you need to do in order to read a compressed header block from various PUSH_PROMISE, HEADERS, and/or CONTINUATION frames. Once you have a complete block, you can use this library to turn it into something you can use for fulfilling HTTP requests
hpack
provides a single type that represents both encoding and decoding
contexts. A new context can be created with hpack:new_context/0
or
hpack:new_context/1
(passing the size for the HEADER_TABLE_SIZE
setting).
If HTTP/2 settings get renegotiated, you can pass that information
along by calling hpack:new_max_table_size/2
, like this:
NewContext = hpack:new_max_table_size(NewSize, OldContext).
Decode a headers binary with hpack:decode/2
. It's your job to
assemble the binary if it's coming from multiple HTTP/2 frames.
{ok, {Headers, NewContext}} = hpack:decode(Binary, OldContext).
Headers
is a list of headers, where each header is a {binary(), binary()}
tuple.
Encoding headers works the same way, only a [{binary(), binary()}]
goes in, and a binary()
comes out.
{Bin, NewContext} = hpack:encode(Headers, OldContext),
Here's how to do the whole thing!
DecodeContext1 = hpack:new_context(), %% Server context
EncodeContext1 = hpack:new_context(), %% Client context
ClientRequestHeaders = [
{<<":method">>, <<"GET">>},
{<<":path">>, <<"/">>},
{<<"X-Whatev">>, <<"commands">>}
],
%% Client operation
{RequestHeadersBin, EncodeContext2} = hpack:encode(
ClientRequestHeaders,
EncodeContext1),
%% Server operation, after receiving RequestHeadersBin
{ok, {ServerRequestHeaders, DecodeContext2}} = hpack:decode(
RequestHeadersBin,
DecodeContext1),
%% Note the following truths:
ClientRequestHeaders = ServerRequestHeaders,
EncodeContext1 = DecodeContext1,
EncodeContext2 = DecodeContext2.
The whole reason HPACK works is that the client and server both keep their contexts in sync with each other.
Note: I used the terms client
and server
here, but it could as
easily be sender
and receiver
if you're a proxy.