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



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

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:



110
111
112
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
# File 'lib/protocol/hpack/decompressor.rb', line 110

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? ? (read_byte & limit) : 0
	
	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
# File 'lib/protocol/hpack/decompressor.rb', line 93

def read_string
	huffman = (peek_byte & 0x80) == 0x80
	
	length = read_integer(7)
	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