Class: HTTP2::Header::Compressor

Inherits:
Object
  • Object
show all
Includes:
BufferUtils, 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 BufferUtils

#append_str, #read_str, #read_uint32, #shift_byte

Methods included from PackingExtensions

#pack

Constructor Details

#initialize(options = {}) ⇒ Compressor



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

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

Instance Method Details

#encode(headers) ⇒ Buffer

Encodes provided list of HTTP headers.



132
133
134
135
136
137
138
139
140
141
# File 'lib/http/2/header/compressor.rb', line 132

def encode(headers)
  buffer = "".b
  headers.partition { |f, _| f.start_with? ":" }.each do |hs|
    @cc.encode(hs) do |cmd|
      header(cmd, buffer)
    end
  end

  buffer
end

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

Encodes header command with appropriate header representation.



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/http/2/header/compressor.rb', line 99

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

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

    string(h[:value], buffer)
  end

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

  buffer
end

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


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

def integer(i, n, buffer:, offset: buffer.size)
  limit = (1 << 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, buffer = "".b) ⇒ 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


76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/http/2/header/compressor.rb', line 76

def string(str, buffer = "".b)
  case @cc.options[:huffman]
  when :always
    huffman_string(str, buffer)
  when :never
    plain_string(str, buffer)
  else
    huffman = Huffman.encode(str)
    if huffman.bytesize < str.bytesize
      huffman_offset = buffer.bytesize
      append_str(buffer, huffman)
      set_huffman_size(buffer, huffman_offset)
    else
      plain_string(str, buffer)
    end
  end
end

#table_size=(size) ⇒ Object

Set dynamic table size in EncodingContext



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

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