class Compressor
Responsible for encoding header key-value pairs using HPACK algorithm.
Definitions
def write_integer(value, bits)
Encodes provided value via integer representation.
- http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-10#section-5.1
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
Implementation
def write_integer(value, bits)
limit = (1 << bits) - 1
return @buffer << value if value < limit
@buffer << limit unless bits.zero?
value -= limit
while value >= 128
@buffer << ((value & 0x7f) + 128)
value /= 128
end
@buffer << value
end
def write_string(string, huffman = self.huffman)
Encodes provided value via string literal representation.
- http://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
Implementation
def write_string(string, huffman = self.huffman)
if huffman != :never
encoded = Huffman.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
def write_header(command)
Encodes header command with appropriate header representation.
Implementation
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
def encode(headers, table_size = @table_size_limit)
Encodes provided list of HTTP headers.
Implementation
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