Class: HTTP::HPACK::Compressor

Inherits:
Object
  • Object
show all
Defined in:
lib/http/hpack/compressor.rb

Overview

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(buffer, context = Context.new) ⇒ Compressor

Returns a new instance of Compressor.



51
52
53
54
# File 'lib/http/hpack/compressor.rb', line 51

def initialize(buffer, context = Context.new)
  @buffer = buffer
  @context = context
end

Instance Attribute Details

#bufferObject (readonly)

Returns the value of attribute buffer.



56
57
58
# File 'lib/http/hpack/compressor.rb', line 56

def buffer
  @buffer
end

#contextObject (readonly)

Returns the value of attribute context.



57
58
59
# File 'lib/http/hpack/compressor.rb', line 57

def context
  @context
end

#offsetObject (readonly)

Returns the value of attribute offset.



58
59
60
# File 'lib/http/hpack/compressor.rb', line 58

def offset
  @offset
end

Instance Method Details

#encode(headers) ⇒ Buffer

Encodes provided list of HTTP headers.

Parameters:

  • headers (Array)

    [[name, value], …]

Returns:

  • (Buffer)


182
183
184
185
186
187
188
189
190
# File 'lib/http/hpack/compressor.rb', line 182

def encode(headers)
  commands = @context.encode(headers)
  
  commands.each do |command|
    write_header(command)
  end
  
  return @buffer
end

#huffmanObject



102
103
104
# File 'lib/http/hpack/compressor.rb', line 102

def huffman
  @context.options[:huffman]
end

#write_byte(byte) ⇒ Object



60
61
62
# File 'lib/http/hpack/compressor.rb', line 60

def write_byte(byte)
  @buffer << byte.chr
end

#write_bytes(bytes) ⇒ Object



64
65
66
# File 'lib/http/hpack/compressor.rb', line 64

def write_bytes(bytes)
  @buffer << bytes
end

#write_header(command) ⇒ Buffer

Encodes header command with appropriate header representation.

Parameters:

  • h (Hash)

    header command

  • buffer (String)

Returns:

  • (Buffer)


153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/http/hpack/compressor.rb', line 153

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 :changetablesize
    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) ⇒ String

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

Parameters:

  • value (Integer)

    value to encode

  • bits (Integer)

    number of available bits

Returns:

  • (String)

    binary string



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/http/hpack/compressor.rb', line 83

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) ⇒ String

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

Parameters:

  • string (String)

Returns:

  • (String)

    binary string



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/http/hpack/compressor.rb', line 126

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