class Protocol::HPACK::Decompressor

Responsible for decoding received headers and maintaining compression context of the opposing peer. Decompressor must be initialized with appropriate starting context based on local role: client or server.

Attributes

buffer[R]
context[R]
offset[R]
table_size_limit[R]

Public Class Methods

new(buffer, context = Context.new, table_size_limit: nil) click to toggle source
# File lib/protocol/hpack/decompressor.rb, line 33
def initialize(buffer, context = Context.new, table_size_limit: nil)
        @buffer = buffer
        @context = context
        @offset = 0
        
        @table_size_limit = table_size_limit
end

Public Instance Methods

decode(list = []) click to toggle source

Decodes and processes header commands within provided buffer.

@param buffer [Buffer] @return [Array] +[[name, value], …]+

# File lib/protocol/hpack/decompressor.rb, line 155
def decode(list = [])
        while !end?
                command = read_header
                
                if pair = @context.decode(command)
                        list << pair
                end
        end
        
        if command and command[:type] == :change_table_size
                raise CompressionError, "Trailing table size update!"
        end
        
        return list
end
end?() click to toggle source
# File lib/protocol/hpack/decompressor.rb, line 47
def end?
        @offset >= @buffer.bytesize
end
peek_byte() click to toggle source
# File lib/protocol/hpack/decompressor.rb, line 59
def peek_byte
        @buffer.getbyte(@offset)
end
read_byte() click to toggle source
# File lib/protocol/hpack/decompressor.rb, line 51
def read_byte
        if byte = @buffer.getbyte(@offset)
                @offset += 1
        end
        
        return byte
end
read_bytes(length) click to toggle source
# File lib/protocol/hpack/decompressor.rb, line 63
def read_bytes(length)
        slice = @buffer.byteslice(@offset, length)
        
        @offset += length
        
        return slice
end
read_header() click to toggle source

Decodes header command from provided buffer.

@param buffer [Buffer] @return [Hash] command

# File lib/protocol/hpack/decompressor.rb, line 115
def read_header
        pattern = peek_byte

        header = {}
        header[:type], type = HEADER_REPRESENTATION.find do |_t, desc|
                mask = (pattern >> desc[:prefix]) << desc[:prefix]
                mask == desc[:pattern]
        end

        raise CompressionError unless header[:type]

        header[:name] = read_integer(type[:prefix])

        case header[:type]
        when :indexed
                raise CompressionError if header[:name].zero?
                header[:name] -= 1
        when :change_table_size
                header[:value] = header[:name]
                
                if @table_size_limit and header[:value] > @table_size_limit
                        raise CompressionError, "Table size #{header[:value]} exceeds limit #{@table_size_limit}!"
                end
        else
                if (header[:name]).zero?
                        header[:name] = read_string
                else
                        header[:name] -= 1
                end
                
                header[:value] = read_string
        end

        return header
end
read_integer(bits) click to toggle source

Decodes integer value from provided buffer.

@param bits [Integer] number of available bits @return [Integer]

# File lib/protocol/hpack/decompressor.rb, line 75
def read_integer(bits)
        limit = 2**bits - 1
        value = bits.zero? ? 0 : (read_byte & limit)
        
        shift = 0
        
        while byte = read_byte
                value += ((byte & 127) << shift)
                shift += 7
                
                break if (byte & 128).zero?
        end if (value == limit)
        
        return value
end
read_string() click to toggle source

Decodes string value from provided buffer.

@return [String] UTF-8 encoded string @raise [CompressionError] when input is malformed

# File lib/protocol/hpack/decompressor.rb, line 95
def read_string
        huffman = (peek_byte & 0x80) == 0x80
        
        length = read_integer(7)
        
        raise CompressionError, "Invalid string length!" unless length
        
        string = read_bytes(length)
        
        raise CompressionError, "Invalid string length, got #{string.bytesize}, expecting #{length}!" unless string.bytesize == length
        
        string = Huffman.new.decode(string) if huffman
        
        return string.force_encoding(Encoding::UTF_8)
end