diff options
Diffstat (limited to 'lib/plum/hpack/decoder.rb')
-rw-r--r-- | lib/plum/hpack/decoder.rb | 96 |
1 files changed, 53 insertions, 43 deletions
diff --git a/lib/plum/hpack/decoder.rb b/lib/plum/hpack/decoder.rb index 13833c1..b536123 100644 --- a/lib/plum/hpack/decoder.rb +++ b/lib/plum/hpack/decoder.rb @@ -10,42 +10,47 @@ module Plum end def decode(str) - str = str.dup headers = [] - headers << parse!(str) while str.size > 0 - headers.compact + pos = 0 + lpos = str.bytesize + while pos < lpos + l, succ = parse(str, pos) + pos += succ + headers << l if l + end + headers end private - def parse!(str) - first_byte = str.uint8 - if first_byte >= 128 # 0b1XXXXXXX - parse_indexed!(str) - elsif first_byte >= 64 # 0b01XXXXXX - parse_indexing!(str) - elsif first_byte >= 32 # 0b001XXXXX - self.limit = read_integer!(str, 5) - nil + def parse(str, pos) + first_byte = str.uint8(pos) + if first_byte >= 0x80 # 0b1XXXXXXX + parse_indexed(str, pos) + elsif first_byte >= 0x40 # 0b01XXXXXX + parse_indexing(str, pos) + elsif first_byte >= 0x20 # 0b001XXXXX + self.limit, succ = read_integer(str, pos, 5) + [nil, succ] else # 0b0000XXXX (without indexing) or 0b0001XXXX (never indexing) - parse_no_indexing!(str) + parse_no_indexing(str, pos) end end - def read_integer!(str, prefix_length) - first_byte = str.byteshift(1).uint8 - raise HPACKError.new("integer: end of buffer") unless first_byte + def read_integer(str, pos, prefix_length) + raise HPACKError.new("integer: end of buffer") if str.empty? + first_byte = str.uint8(pos) mask = (1 << prefix_length) - 1 ret = first_byte & mask - return ret if ret < mask + return [ret, 1] if ret != mask octets = 0 - while next_value = str.byteshift(1).uint8 - ret += (next_value & 0b01111111) << (7 * octets) + while next_value = str.uint8(pos + octets + 1) + ret += (next_value % 0x80) << (7 * octets) octets += 1 - if next_value < 128 - return ret + if next_value < 0x80 + return [ret, 1 + octets] elsif octets == 4 # RFC 7541 5.1 tells us that we MUST have limitation. at least > 2 ** 28 raise HPACKError.new("integer: too large integer") end @@ -54,29 +59,32 @@ module Plum raise HPACKError.new("integer: end of buffer") end - def read_string!(str) - first_byte = str.uint8 - raise HPACKError.new("string: end of buffer") unless first_byte + def read_string(str, pos) + raise HPACKError.new("string: end of buffer") if str.empty? + first_byte = str.uint8(pos) - huffman = (first_byte >> 7) == 1 - length = read_integer!(str, 7) - bin = str.byteshift(length) + huffman = first_byte > 0x80 + length, ilen = read_integer(str, pos, 7) + raise HTTPError.new("string: end of buffer") if str.bytesize < length - raise HTTPError.new("string: end of buffer") if bin.bytesize < length - bin = Huffman.decode(bin) if huffman - bin + bin = str.byteslice(pos + ilen, length) + if huffman + [Huffman.decode(bin), ilen + length] + else + [bin, ilen + length] + end end - def parse_indexed!(str) + def parse_indexed(str, pos) # indexed # +---+---+---+---+---+---+---+---+ # | 1 | Index (7+) | # +---+---------------------------+ - index = read_integer!(str, 7) - fetch(index) + index, succ = read_integer(str, pos, 7) + [fetch(index), succ] end - def parse_indexing!(str) + def parse_indexing(str, pos) # +---+---+---+---+---+---+---+---+ # | 0 | 1 | Index (6+) | # +---+---+-----------------------+ @@ -96,20 +104,21 @@ module Plum # +---+---------------------------+ # | Value String (Length octets) | # +-------------------------------+ - index = read_integer!(str, 6) + index, ilen = read_integer(str, pos, 6) if index == 0 - name = read_string!(str) + name, nlen = read_string(str, pos + ilen) else name, = fetch(index) + nlen = 0 end - val = read_string!(str) + val, vlen = read_string(str, pos + ilen + nlen) store(name, val) - [name, val] + [[name, val], ilen + nlen + vlen] end - def parse_no_indexing!(str) + def parse_no_indexing(str, pos) # +---+---+---+---+---+---+---+---+ # | 0 | 0 | 0 |0,1| Index (4+) | # +---+---+-----------------------+ @@ -129,16 +138,17 @@ module Plum # +---+---------------------------+ # | Value String (Length octets) | # +-------------------------------+ - index = read_integer!(str, 4) + index, ilen = read_integer(str, pos, 4) if index == 0 - name = read_string!(str) + name, nlen = read_string(str, pos + ilen) else name, = fetch(index) + nlen = 0 end - val = read_string!(str) + val, vlen = read_string(str, pos + ilen + nlen) - [name, val] + [[name, val], ilen + nlen + vlen] end end end |