HPACK

HPACK is the HTTP 2 header compression scheme. This is a specialized algorithm and does not allow for arbitrary compression schemes.

HPACK is implemented in the module HTTP2.HPack.

Header compression and decompression is implemented by compressing or decompressing data as it is first inserted to or read from an IO stream. As the specification is rigid and does not allow for any sort of custom data or metadata, the implementation is not particularly flexible.

API

HTTP2.HPackModule
HPack

Header compression and decompression library for HTTP 2.

The specification can be found here.

HTTP2.HPack.BitWindowType
BitWindow

Data structure representing a range of bits in a single byte. The left boundary β.j₁ gives the first bit (according to 1-based indexing) and the right boundary w.j₂ gives the last. For example, the least significant 4 bits of the byte 0xff can be represented with BitWindow(0xff, 4, 8).

Relevant Methods

  • UInt8(β::BitWindow): Convert the bit window to a UInt8, that is, it removes bits outside of the specified range and moves them to the least significant end of the byte.
  • pushbits(o::UInt, β::BitWindow): Append the bits of the bit window to the integer o, moving exiting bits to more significant digits.
HTTP2.HPack.BytesWindowType
BytesWindow

Data structure representing a range of bits in an array of bytes. The left boundary w.j₁ gives the first bit (according to 1-based indexing) and the right boundary w.j₂ gives the last. For example, the entirety of the 1-byte array v = [0xff] can be described with BytesWindow(v, 1, 8).

This struct is used for decoding HPACK Huffman code.

Relevant Methods

  • UInt(w::BytesWindow): Converts the entire window into a single UInt object. The length of the window must not exceed 8sizeof(UInt) or bits will be lost.
  • extend(w::BytesWindow): Create a new BytesWindow with the latter index incremented by 1.
HTTP2.HPack.DecodeTableType
DecodeTable

Data structure containing an HPACK decoding table. This includes the entries from the static table in the specification as well as dynamic entries added when writing

DecodeTable is thread safe, retrieving and setting entries are atomic operations.

Constructors

DecodeTable(;max_size=typemax(Int32))

Arguments

  • max_size: The maximum table size according to the definition given in the specification.

Indexing

Header fields are retrieved using their index in the table, for example

dt = DecodeTable()

dt[1] # returns ":authority"=>""
HTTP2.HPack.DecoderType
Decoder

Data structure for reading compressed HPACK headers.

Constructors

Decoder(;max_table_size=typemax(Int32))

Arguments

  • max_table_size: The maximum dynamic table size according to the definition given in the specification.

Relevant Methods

  • read(io, d, n): Read at most n bytes from the stream io using the decoder d.

WARNING: the byte limit n is not a "hard" limit in that it is not checked on every byte read. Instead, it merely guarantees that no new header fields will be read past the nth byte.

HTTP2.HPack.EncodeTableType
EncodeTable

Data structure containing an HPACK encoding table. This includes the entries from the static table in the specification as well as dynamic entries added when writing.

EncodeTable is thread safe, retrieving and setting entries are atomic operations.

Constructors

EncodeTable(;max_size=typemax(Int32))

Arguments

  • max_size: The maximum table size according to the definition given in the specification.

Indexing

EncodeTable objects can be indexed with either a header key or a header field. The table index is returned. For example:

et = EncodeTable()

et[":method"=>"POST"] # returns 3
et[":method"]  # returns 2, the index of the first table entry with the `":method"` key.

get(et, "bogus", missing)  # can use get for returning defaults as with dicts
HTTP2.HPack.EncoderType
Encoder{ℰ<:StringEncoder}

Data structure for writing compressed HPACK headers.

Constructors

Encoder(; never_index=Set{String}(), max_table_size=typemax(Int32))

Arguments

  • never_index: A set of header keys as strings which should never be indexed in the table in memory.
  • max_table_size: The maximum dynamic table size. The size definition is somewhat bizarre, see here for how it is defined.

Relevant Methods

  • write(io, e, kv): Writes the header field or fields kv to the stream io using encoder e.
HTTP2.HPack.HuffmanDecoderType
HuffmanDecoder()

Data structure for decoding HPACK Huffman encoded strings. By default, this uses the static Huffman code given in the HPACK specification.

HuffmanDecoder()[l, c] gets the value of code word c of length l.

Strings can be read with readstring(io, HuffmanDecoder()).

HTTP2.HPack.IdentityStringDecoderType
IdentityStringDecoder()

Singleton data structure used for trivially decoding strings in HPACK headers. This is simply used for moving bytes to Julia String objects, no decompression nor any other type of transformation is applied.

HTTP2.HPack.IdentityStringEncoderType
IdentityStringEncoder()

Singleton data structure used for trivially encoding strings in HPACK headers. Strings encoded with this encoder appear exactly as they do in memory and do not undergo any compression or any other type of transformation.

HTTP2.HPack.TranscoderType
Transcoder{ℰ<:StringEncoder}

Data structure for reading and writing of HPACK headers. This is a simple convenience wrapper around HPack.Encoder and HPack.Decoder.

Base.writeMethod
write(io::IO, h::HuffmanEncoder, s)

Write the string s to io using the Huffman encoding of h. This is for encoding strings in HPACK headers, and can be used directly, that is, it writes the encoding length integer first, with the leading bit set to 1 (this signals to HPACK implementations that the string is Huffman encoded).

HTTP2.HPack.decodeMethod
decode(d::Union{Decoder,Transcoder}, data::AbstractVector{UInt8})

Decode HPACK data from an array of bytes.

HTTP2.HPack.encodeMethod
encode(e::Union{Encoder,Transcoder}, ϕ; literal_write=literal)

Encode header fields ϕ (either a Pair{String,String} or iterator thereof) using method literal_write for writing literals returning a Vector{UInt8} of HPACK encoded data.

HTTP2.HPack.entry!Method
entry!(et::EncodeTable, kv::AbstractHeaderField)

Insert the header field kv into the table. As per the specification, it is always added as the first entry of the dynamic table, evicting the last entries as needed to satisfy the maximum size constraint.

HTTP2.HPack.entrysizeMethod
entrysize(kv::HeaderField)

The "size" of a header entry according to the HPACK specification. This is defined as the sum of the size of the strings plus, for some reason, 32, don't ask me why.

HTTP2.HPack.evict!Function
evict!(et::EncodeTable, n=1)

Evict n entries from the HPACK encoding table et.

HTTP2.HPack.huffmanlookuptreeMethod
huffmanlookuptree(v, ℓ)

Given huffman code v and word length , generate a nested dictionary allowing for efficient lookups of huffman words.

For the outer dict, the keys are the word lengths and the values are the inner dicts. The inner dicts have the words as keys with indices as values.

This is used to generate HPack.HUFFMAN_LOOKUP_TREE and under normal circumstances is never called at run time.

HTTP2.HPack.indexMethod
index

Decompress a header field by referring to an indexed table entry.

HTTP2.HPack.literalMethod
literal

Decompress a literal header field, possibly by referring to the key as an indexed table entry, but with the value given literally. Results in adding the header field as an entry to the dynamic table.

HTTP2.HPack.literalneverindexMethod
literalneverindex

Decompress a literal header field, possibly by referring to the key as an indexed table entry, but forbidding the value from ever being stored in the dynamic table.

HTTP2.HPack.literalnoindexMethod
literalnoindex

Decompress a literal header field, possibly by referring to the key as an indexed table entry, but without creating any new table entries.

HTTP2.HPack.read_integerMethod
read_integer(io, b, p)

Read an integer from the stream. The first byte b must be provided, along with the prefix length p.

Returns integer, number of bytes read.

HTTP2.HPack.readentryMethod
readentry(io, d::Decoder)

Read a single entry from the stream returning the entry and the number of bytes read.

HTTP2.HPack.readnextwordMethod
readnextword(v::AbstractVector{UInt8}, j::Int, d::HuffmanDecoder)

Attempt to read the next word starting at bit j in a Huffman code given in v. Returns a tuple of a found code word and the next bit which has not yet been read. If no valid code word can be found starting at j the first return value is nothing.

HTTP2.HPack.setmaxsize!Method
setmaxsize!(t, s::Integer)

Set the maximum size of the table t, evicting entries as necessary.

HTTP2.HPack.shouldneverindexMethod
shouldneverindex(e::Encoder, kv::AbstractHeaderField)

Returns true iff the encoder e specifies that the header field should never be indexed.

HTTP2.HPack.tablesizeMethod
tablesize(et::EncodeTable)
tablesize(dt::DecodeTable)

Computes the "table size" as defined in the HPACK specification. This is computed by summing entrysize for all entries. This counts only the size of the dynamic table, not the static table.

HTTP2.HPack.tablesizeMethod
tablesize(ed)

Gives the current dynamic table size of the HPACK encoder or decoder ed. The definition of the size is somewhat bizarre, see here for the exact definition.

HTTP2.HPack.write_huffman_integerMethod
write_huffman_integer(io, b, p, n, ℓ)

Write the Huffman code word n of length bits to io starting with a p bit prefix on the byte b (that is, p bits are available to be written on b).

Note that this requires p > 1, otherwise you will get a bogus answer. No check for this is performed.

HTTP2.HPack.write_huffman_stringMethod
write_huffman_string(io, h, v)

Write the string v to io using the Huffman encoder h. This does NOT include the encoding length integer.

If the final byte contains unused bits, these are filled with 1s. While the HPACK header does not explicitly indicate this is required, there is no Huffman word for 7 1s and it can be seen in examples. Alignment of the end of the code to the final byte is required by the specification.