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.



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

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.



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

def buffer
  @buffer
end

#contextObject (readonly)

Returns the value of attribute context.



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

def context
  @context
end

#offsetObject (readonly)

Returns the value of attribute offset.



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

def offset
  @offset
end

#table_size_limitObject (readonly)

Returns the value of attribute table_size_limit.



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

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], …]



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

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)


47
48
49
# File 'lib/protocol/hpack/decompressor.rb', line 47

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

#peek_byteObject



59
60
61
# File 'lib/protocol/hpack/decompressor.rb', line 59

def peek_byte
	@buffer.getbyte(@offset)
end

#read_byteObject



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

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

#read_bytes(length) ⇒ Object



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

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:



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
148
149
# File 'lib/protocol/hpack/decompressor.rb', line 115

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)


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

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:



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

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