HPACK Implementation for Erlang
Erlang Makefile

README.md

hpack

HPACK Implementation for Erlang

HPACK RFC-7541

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).

Why Separate?

  • Use in other projects
  • A separate RFC seemed like a really clear abstraction

What's Covered

What's not covered

HTTP/2 Frames

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

API

Creating Contexts

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).

Changing table size

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).

Decoding Headers

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

Encoding headers works the same way, only a [{binary(), binary()}] goes in, and a binary() comes out.

{Bin, NewContext} = hpack:encode(Headers, OldContext),

Soup to nuts

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
{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.