Class: Protocol::HPACK::Decompressor

Inherits:
Object
  • Object
show all
Defined in:
lib/protocol/hpack/decompressor.rb

Overview

Responsible for decoding received headers and maintaining compression context of the opposing peer. Decompressor must be initialized with appropriate starting context based on local role: client or server.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(buffer, context = Context.new, table_size_limit: nil) ⇒ Decompressor

Returns a new instance of Decompressor.



31
32
33
34
35
36
37
# File 'lib/protocol/hpack/decompressor.rb', line 31

def initialize(buffer, context = Context.new, table_size_limit: nil)
  @buffer = buffer
  @context = context
  @offset = 0
  
  @table_size_limit = table_size_limit
end

Instance Attribute Details

#bufferObject (readonly)

Returns the value of attribute buffer.



39
40
41
# File 'lib/protocol/hpack/decompressor.rb', line 39

def buffer
  @buffer
end

#contextObject (readonly)

Returns the value of attribute context.



40
41
42
# File 'lib/protocol/hpack/decompressor.rb', line 40

def context
  @context
end

#offsetObject (readonly)

Returns the value of attribute offset.



41
42
43
# File 'lib/protocol/hpack/decompressor.rb', line 41

def offset
  @offset
end

#table_size_limitObject (readonly)

Returns the value of attribute table_size_limit.



43
44
45
# File 'lib/protocol/hpack/decompressor.rb', line 43

def table_size_limit
  @table_size_limit
end

Instance Method Details

#decode(list = []) ⇒ Array

Decodes and processes header commands within provided buffer.

Parameters:

  • buffer (Buffer)

Returns:

  • (Array)

    [[name, value], …]



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/protocol/hpack/decompressor.rb', line 153

def decode(list = [])
  while !end?
    command = read_header
    
    if pair = @context.decode(command)
      list << pair
    end
  end
  
  if command and command[:type] == :change_table_size
    raise CompressionError, "Trailing table size update!"
  end
  
  return list
end

#end?Boolean

Returns:

  • (Boolean)


45
46
47
# File 'lib/protocol/hpack/decompressor.rb', line 45

def end?
  @offset >= @buffer.bytesize
end

#peek_byteObject



57
58
59
# File 'lib/protocol/hpack/decompressor.rb', line 57

def peek_byte
  @buffer.getbyte(@offset)
end

#read_byteObject



49
50
51
52
53
54
55
# File 'lib/protocol/hpack/decompressor.rb', line 49

def read_byte
  if byte = @buffer.getbyte(@offset)
    @offset += 1
  end
  
  return byte
end

#read_bytes(length) ⇒ Object



61
62
63
64
65
66
67
# File 'lib/protocol/hpack/decompressor.rb', line 61

def read_bytes(length)
  slice = @buffer.byteslice(@offset, length)
  
  @offset += length
  
  return slice
end

#read_headerHash

Decodes header command from provided buffer.

Parameters:

  • buffer (Buffer)

Returns:

  • (Hash)

    command

Raises:



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/protocol/hpack/decompressor.rb', line 113

def read_header
  pattern = peek_byte

  header = {}
  header[:type], type = HEADER_REPRESENTATION.find do |_t, desc|
    mask = (pattern >> desc[:prefix]) << desc[:prefix]
    mask == desc[:pattern]
  end

  raise CompressionError unless header[:type]

  header[:name] = read_integer(type[:prefix])

  case header[:type]
  when :indexed
    raise CompressionError if header[:name].zero?
    header[:name] -= 1
  when :change_table_size
    header[:value] = header[:name]
    
    if @table_size_limit and header[:value] > @table_size_limit
      raise CompressionError, "Table size #{header[:value]} exceeds limit #{@table_size_limit}!"
    end
  else
    if (header[:name]).zero?
      header[:name] = read_string
    else
      header[:name] -= 1
    end
    
    header[:value] = read_string
  end

  return header
end

#read_integer(bits) ⇒ Integer

Decodes integer value from provided buffer.

Parameters:

  • bits (Integer)

    number of available bits

Returns:

  • (Integer)


73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/protocol/hpack/decompressor.rb', line 73

def read_integer(bits)
  limit = 2**bits - 1
  value = bits.zero? ? 0 : (read_byte & limit)
  
  shift = 0
  
  while byte = read_byte
    value += ((byte & 127) << shift)
    shift += 7
    
    break if (byte & 128).zero?
  end if (value == limit)
  
  return value
end

#read_stringString

Decodes string value from provided buffer.

Returns:

  • (String)

    UTF-8 encoded string

Raises:



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/protocol/hpack/decompressor.rb', line 93

def read_string
  huffman = (peek_byte & 0x80) == 0x80
  
  length = read_integer(7)
  
  raise CompressionError, "Invalid string length!" unless length
  
  string = read_bytes(length)
  
  raise CompressionError, "Invalid string length, got #{string.bytesize}, expecting #{length}!" unless string.bytesize == length
  
  string = Huffman.new.decode(string) if huffman
  
  return string.force_encoding(Encoding::UTF_8)
end