iyi

Standard library, 0.18.0

std/hpack

Standard HPACK: Header Compression for HTTP/2 (RFC 7541)

import std/hpack to use it. Source: src/std/hpack.iyi at v0.18.0.

Implements:

  • RFC 7541 61-entry Static Table (Appendix A)
  • Dynamic Table with FIFO eviction by size (Section 4)
  • N-bit prefix integer encoding and decoding for 1..8 bits (Section 5.1)
  • String literals, both raw and canonical Huffman-coded (Section 5.2)
  • Canonical Huffman table (Appendix B, 257 symbols)
  • Prefix-tree / state-machine Huffman decoder with strict padding validation:
  * Rejects EOS symbol in stream
  * Rejects padding longer than 7 bits
  * Rejects padding containing 0 bits (must be all 1s)
  • Four field representations (Section 6):
  * Indexed Header Field (6.1)
  * Literal Header Field with Incremental Indexing (6.2.1)
  * Literal Header Field without Indexing (6.2.2)
  * Literal Header Field Never Indexed (6.2.3)
  • Dynamic Table Size Update (Section 6.3), including RFC 4.2:
  * encoder emits the interval minimum then the final size
  * decoder refuses a size update after a header field
  • Security defenses:
  * Decompression bomb protection: bounds the decoded header list size (RFC 7540 6.5.2: name, value and 32 octets a field) & count
  * Respects peer SETTINGS_HEADER_TABLE_SIZE limit
  * Invalid index detection (index 0 or index > static + dynamic length)
  * Integer encodings that overflow UInt64 or Int32 indexes are HpackError
  * Never-indexed representation honoured on re-encode

Bytesalias

std/hpackiyi doc src/std/hpack.iyi
pub alias Bytes = Std::Slice::Bytes

Decoderclass

std/hpackiyi doc src/std/hpack.iyi
pub class Decoder
  def decode(payload : Bytes) : Array(HeaderField) | HpackError
  def dynamic_table : DynamicTable
  def initialize(max_table_size : Int32 = 4096, protocol_max_table_size : Int32 = 4096, max_header_list_size : Int32 = 65536, max_header_count : Int32 = 1000)
  def max_header_count : Int32
  def max_header_count=(value : Int32) : Int32
  def max_header_list_size : Int32
  def max_header_list_size=(value : Int32) : Int32
  def protocol_max_table_size : Int32
  # A SETTINGS_HEADER_TABLE_SIZE change reaches the table too: the limit
  # was only this field's, and the table went on refusing an update above
  # the size it was made with.
  def protocol_max_table_size=(limit : Int32) : Nil
end

DynamicTableclass

std/hpackiyi doc src/std/hpack.iyi
pub class DynamicTable
  def add(name : String, value : String, never_indexed : Bool = false) : Nil
  def add(field : HeaderField) : Nil
  def empty? : Bool
  def entries : Array(HeaderField)
  def find(name : String, value : String) : Tuple(Int32, Bool)
  def find_name(name : String) : Int32
  def get(relative_index : Int32) : HeaderField | ::Nil
  # A table is made within the limit it is resized within: made above it,
  # an encoder's table outgrew the peer's, and an Encoder made with 8192
  # sent blocks a default Decoder refused ("Index 121 out of range (max:
  # 103)") while `set_max_size(8192)` on it was refused.
  def initialize(max_size : Int32 = 4096, settings_max_size : Int32 = 4096)
  def length : Int32
  def max_size : Int32
  def set_max_size(new_max : Int32) : Nil | HpackError
  def settings_max_size : Int32
  # The protocol's limit (SETTINGS_HEADER_TABLE_SIZE) as it changes during
  # a connection; the size in use still moves only by `set_max_size`.
  def settings_max_size=(limit : Int32) : Nil
  def size : Int32
  def self.size_error(new_max : Int32, limit : Int32) : HpackError | ::Nil
end

Encoderclass

std/hpackiyi doc src/std/hpack.iyi
pub class Encoder
  def dynamic_table : DynamicTable
  def encode(headers : Array(HeaderField), force_huffman : Bool | ::Nil = nil) : Bytes
  def initialize(max_table_size : Int32 = 4096, protocol_max_table_size : Int32 = 4096, use_huffman : Bool = true)
  def protocol_max_table_size : Int32
  # A SETTINGS_HEADER_TABLE_SIZE change reaches the table too: the limit
  # was only this field's, so a raised one was refused by the table and a
  # lowered one left the table above it (RFC 7541 4.2), with no update.
  # A lowered limit is always signalled: the peer's table may still be the
  # size the old limit let it start at, which this encoder never sent.
  def protocol_max_table_size=(limit : Int32) : Nil
  def set_max_table_size(new_max : Int32) : Nil | HpackError
  def use_huffman : Bool
  def use_huffman=(value : Bool) : Bool
end

HeaderFieldstruct

std/hpackiyi doc src/std/hpack.iyi
pub struct HeaderField
  def ==(other : HeaderField) : Bool
  def indexing : IndexingMode
  def initialize(name : String, value : String, indexing : IndexingMode = IndexingMode::Incremental)
  def initialize(name : String, value : String, never_indexed : Bool)
  def name : String
  def never_indexed : Bool
  def size : Int32
  def to_s : String
  def value : String
end

HpackErrorstruct

std/hpackiyi doc src/std/hpack.iyi
pub struct HpackError
  def initialize(message : String)
  def to_s : String
end

Huffmanclass

std/hpackiyi doc src/std/hpack.iyi
pub class Huffman
  def self.decode(bytes : Bytes, offset : Int32, length : Int32) : String | HpackError
  def self.decode(bytes : Bytes) : String | HpackError
  def self.encode(input : String, buffer : Array(UInt8)) : Nil
  def self.encode(input : String) : Bytes
  def self.encode(input : Bytes) : Bytes
  def self.encoded_size(input : String) : Int32
end

IndexingModestruct

std/hpackiyi doc src/std/hpack.iyi
pub struct IndexingMode
  def ==(other : IndexingMode) : Bool
  def initialize(value : Int32)
  def value : Int32
end

Integerclass

std/hpackiyi doc src/std/hpack.iyi
pub class Integer
  def self.decode(bytes : Bytes, offset : Int32, prefix_bits : Int32) : Tuple(UInt64, Int32) | HpackError
  def self.encode(val : UInt64, prefix_bits : Int32, prefix_byte : UInt8, buffer : Array(UInt8)) : Nil
  def self.encode(val : UInt64, prefix_bits : Int32, prefix_byte : UInt8 = 0_u8) : Bytes
end

IntegerCodecalias

std/hpackiyi doc src/std/hpack.iyi
pub alias IntegerCodec = Integer

StaticTableclass

std/hpackiyi doc src/std/hpack.iyi
pub class StaticTable
  def self.find(name : String, value : String) : Tuple(Int32, Bool)
  def self.find_name(name : String) : Int32
  def self.get(index : Int32) : HeaderField | ::Nil
  def self.size : Int32
end

Tableclass

std/hpackiyi doc src/std/hpack.iyi
pub class Table
  def self.find(dynamic_table : DynamicTable, name : String, value : String) : Tuple(Int32, Bool)
  def self.find_name(dynamic_table : DynamicTable, name : String) : Int32
  def self.get(dynamic_table : DynamicTable, global_index : Int32) : HeaderField | ::Nil
end

Error for HpackErrorimpl

std/hpackiyi doc src/std/hpack.iyi
impl Error for HpackError
  def message : String
end