class Protocol::HPACK::Compressor

Responsible for encoding header key-value pairs using HPACK algorithm.

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/compressor.rb, line 53
def initialize(buffer, context = Context.new, table_size_limit: nil)
        @buffer = buffer
        @context = context
        
        @table_size_limit = table_size_limit
end

Public Instance Methods

encode(headers, table_size = @table_size_limit) click to toggle source

Encodes provided list of HTTP headers.

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

# File lib/protocol/hpack/compressor.rb, line 188
def encode(headers, table_size = @table_size_limit)
        if table_size and table_size != @context.table_size
                command = @context.change_table_size(table_size)
                
                write_header(command)
        end
        
        commands = @context.encode(headers)
        
        commands.each do |command|
                write_header(command)
        end
        
        return @buffer
end
huffman() click to toggle source
# File lib/protocol/hpack/compressor.rb, line 108
def huffman
        @context.huffman
end
write_byte(byte) click to toggle source
# File lib/protocol/hpack/compressor.rb, line 66
def write_byte(byte)
        @buffer << byte.chr
end
write_bytes(bytes) click to toggle source
# File lib/protocol/hpack/compressor.rb, line 70
def write_bytes(bytes)
        @buffer << bytes
end
write_header(command) click to toggle source

Encodes header command with appropriate header representation.

@param h [Hash] header command @param buffer [String] @return [Buffer]

# File lib/protocol/hpack/compressor.rb, line 159
def write_header(command)
        representation = HEADER_REPRESENTATION[command[:type]]
        
        first = @buffer.bytesize
        
        case command[:type]
        when :indexed
                write_integer(command[:name] + 1, representation[:prefix])
        when :change_table_size
                write_integer(command[:value], representation[:prefix])
        else
                if command[:name].is_a? Integer
                        write_integer(command[:name] + 1, representation[:prefix])
                else
                        write_integer(0, representation[:prefix])
                        write_string(command[:name])
                end
                
                write_string(command[:value])
        end

        # set header representation pattern on first byte
        @buffer.setbyte(first, @buffer.getbyte(first) | representation[:pattern])
end
write_integer(value, bits) click to toggle source

Encodes provided value via integer representation.

If I < 2^N - 1, encode I on N bits
Else
    encode 2^N - 1 on N bits
    I = I - (2^N - 1)
    While I >= 128
         Encode (I % 128 + 128) on 8 bits
         I = I / 128
    encode (I) on 8 bits

@param value [Integer] value to encode @param bits [Integer] number of available bits @return [String] binary string

# File lib/protocol/hpack/compressor.rb, line 89
def write_integer(value, bits)
        limit = 2**bits - 1
        
        return write_bytes([value].pack('C')) if value < limit
        
        bytes = []
        bytes.push(limit) unless bits.zero?
        
        value -= limit
        while value >= 128
                bytes.push((value % 128) + 128)
                value /= 128
        end
        
        bytes.push(value)
        
        write_bytes(bytes.pack('C*'))
end
write_string(string, huffman = self.huffman) click to toggle source

Encodes provided value via string literal representation.

  • tools.ietf.org/html/draft-ietf-httpbis-header-compression-10#section-5.2

  • The string length, defined as the number of bytes needed to store its UTF-8 representation, is represented as an integer with a seven bits prefix. If the string length is strictly less than 127, it is represented as one byte.

  • If the bit 7 of the first byte is 1, the string value is represented as a list of Huffman encoded octets (padded with bit 1's until next octet boundary).

  • If the bit 7 of the first byte is 0, the string value is represented as a list of UTF-8 encoded octets.

+@options [:huffman]+ controls whether to use Huffman encoding:

:never   Do not use Huffman encoding
:always  Always use Huffman encoding
:shorter Use Huffman when the result is strictly shorter

@param string [String] @return [String] binary string

# File lib/protocol/hpack/compressor.rb, line 132
def write_string(string, huffman = self.huffman)
        if huffman != :never
                encoded = Huffman.new.encode(string)
                
                if huffman == :shorter and encoded.bytesize >= string.bytesize
                        encoded = nil
                end
        end
        
        if encoded
                first = @buffer.bytesize
                
                write_integer(encoded.bytesize, 7)
                write_bytes(encoded.b)
                
                @buffer.setbyte(first, @buffer.getbyte(first).ord | 0x80)
        else
                write_integer(string.bytesize, 7)
                write_bytes(string.b)
        end
end