Class: HTTP2::Header::Compressor

Inherits:
Object
  • Object
show all
Includes:
PackingExtensions
Defined in:
lib/http/2/header/compressor.rb

Overview

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

Instance Method Summary collapse

Methods included from PackingExtensions

#pack

Constructor Details

#initialize(options = {}) ⇒ Compressor

Returns a new instance of Compressor.

Parameters:

  • options (Hash) (defaults to: {})

    encoding options



10
11
12
# File 'lib/http/2/header/compressor.rb', line 10

def initialize(options = {})
  @cc = EncodingContext.new(options)
end

Instance Method Details

#encode(headers) ⇒ Buffer

Encodes provided list of HTTP headers.

Parameters:

  • headers (Array)

    [[name, value], …]

Returns:

  • (Buffer)


124
125
126
127
128
129
130
131
132
133
134
# File 'lib/http/2/header/compressor.rb', line 124

def encode(headers)
  buffer = "".b
  pseudo_headers, regular_headers = headers.partition { |f, _| f.start_with? ":" }
  headers = [*pseudo_headers, *regular_headers]
  commands = @cc.encode(headers)
  commands.each do |cmd|
    buffer << header(cmd)
  end

  buffer
end

#header(h, buffer = "".b) ⇒ Buffer

Encodes header command with appropriate header representation.

Parameters:

  • h (Hash)

    header command

  • buffer (String) (defaults to: "".b)

Returns:

  • (Buffer)


94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/http/2/header/compressor.rb', line 94

def header(h, buffer = "".b)
  rep = HEADREP[h[:type]]

  case h[:type]
  when :indexed
    integer(h[:name] + 1, rep[:prefix], buffer: buffer)
  when :changetablesize
    integer(h[:value], rep[:prefix], buffer: buffer)
  else
    if h[:name].is_a? Integer
      integer(h[:name] + 1, rep[:prefix], buffer: buffer)
    else
      integer(0, rep[:prefix], buffer: buffer)
      buffer << string(h[:name])
    end

    buffer << string(h[:value])
  end

  # set header representation pattern on first byte
  fb = buffer.ord | rep[:pattern]
  buffer.setbyte(0, fb)

  buffer
end

#integer(i, n, buffer:, offset: 0) ⇒ 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:

  • i (Integer)

    value to encode

  • n (Integer)

    number of available bits

  • buffer (String)

    buffer to pack bytes into

  • offset (Integer) (defaults to: 0)

    offset to insert packed bytes in buffer

Returns:

  • (String)

    binary string



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/http/2/header/compressor.rb', line 37

def integer(i, n, buffer:, offset: 0)
  limit = (2**n) - 1
  return pack([i], "C", buffer: buffer, offset: offset) if i < limit

  bytes = []
  bytes.push limit unless n.zero?

  i -= limit
  while i >= 128
    bytes.push((i % 128) + 128)
    i /= 128
  end

  bytes.push i
  pack(bytes, "C*", buffer: buffer, offset: offset)
end

#string(str) ⇒ 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:

  • str (String)

Returns:

  • (String)

    binary string



74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/http/2/header/compressor.rb', line 74

def string(str)
  case @cc.options[:huffman]
  when :always
    huffman_string(str)
  when :never
    plain_string(str)
  else
    huffman = huffman_string(str)

    plain = plain_string(str)

    huffman.bytesize < plain.bytesize ? huffman : plain
  end
end

#table_size=(size) ⇒ Object

Set dynamic table size in EncodingContext

Parameters:

  • size (Integer)

    new dynamic table size



16
17
18
# File 'lib/http/2/header/compressor.rb', line 16

def table_size=(size)
  @cc.table_size = size
end