From c8551ca9b98110322d48fb536b3fdcc65bcd423b Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Thu, 13 Aug 2015 11:32:48 +0900 Subject: hpack: use tables on encoding --- lib/plum/hpack/context.rb | 9 +++++++ lib/plum/hpack/decoder.rb | 5 +++- lib/plum/hpack/encoder.rb | 66 +++++++++++++++++++++++++++++++++++++---------- 3 files changed, 66 insertions(+), 14 deletions(-) (limited to 'lib/plum/hpack') diff --git a/lib/plum/hpack/context.rb b/lib/plum/hpack/context.rb index 0d0eb63..5973d94 100644 --- a/lib/plum/hpack/context.rb +++ b/lib/plum/hpack/context.rb @@ -33,6 +33,15 @@ module Plum end end + def search(name, value) + if value + i = STATIC_TABLE.index([name, value]) || @dynamic_table.index([name, value]) + else + i = STATIC_TABLE.index {|n, _| n == name } || @dynamic_table.index {|n, _| n == name } + end + i && (i + 1) # index is >= 1 + end + def evict while @limit && @size > @limit name, value = @dynamic_table.pop diff --git a/lib/plum/hpack/decoder.rb b/lib/plum/hpack/decoder.rb index 71c8be5..13833c1 100644 --- a/lib/plum/hpack/decoder.rb +++ b/lib/plum/hpack/decoder.rb @@ -55,7 +55,10 @@ module Plum end def read_string!(str) - huffman = (str.uint8 >> 7) == 1 + first_byte = str.uint8 + raise HPACKError.new("string: end of buffer") unless first_byte + + huffman = (first_byte >> 7) == 1 length = read_integer!(str, 7) bin = str.byteshift(length) diff --git a/lib/plum/hpack/encoder.rb b/lib/plum/hpack/encoder.rb index 551ec58..58d13be 100644 --- a/lib/plum/hpack/encoder.rb +++ b/lib/plum/hpack/encoder.rb @@ -9,10 +9,24 @@ module Plum super end - # currently only support 0x0000XXXX type (without indexing) - # and not huffman + def encode(headers) + out = "" + headers.each do |name, value| + name = name.to_s; value = value.to_s + if index = search(name, value) + out << encode_indexed(index) + elsif index = search(name, nil) + out << encode_half_indexed(index, value, true) # incremental indexing + else + out << encode_literal(name, value, true) # incremental indexing + end + end + out.force_encoding(Encoding::BINARY) + end + + private # +---+---+---+---+---+---+---+---+ - # | 0 | 0 | 0 | 0 | 0 | + # | 0 | 1 | 0 | # +---+---+-----------------------+ # | H | Name Length (7+) | # +---+---------------------------+ @@ -22,17 +36,43 @@ module Plum # +---+---------------------------+ # | Value String (Length octets) | # +-------------------------------+ - def encode(headers) - out = "" - headers.each do |name, value| - out << "\x00" - out << encode_string(name.to_s) - out << encode_string(value.to_s) + def encode_literal(name, value, indexing = true) + if indexing + store(name, value) + fb = "\x40" + else + fb = "\x00" end - out + fb << encode_string(name) << encode_string(value) + end + + # +---+---+---+---+---+---+---+---+ + # | 0 | 1 | Index (6+) | + # +---+---+-----------------------+ + # | H | Value Length (7+) | + # +---+---------------------------+ + # | Value String (Length octets) | + # +-------------------------------+ + def encode_half_indexed(index, value, indexing = true) + if indexing + store(fetch(index)[0], value) + fb = encode_integer(index, 6) + fb.setbyte(0, fb.uint8 | 0b01000000) + else + fb = encode_integer(index, 4) + end + fb << encode_string(value) + end + + # +---+---+---+---+---+---+---+---+ + # | 1 | Index (7+) | + # +---+---------------------------+ + def encode_indexed(index) + s = encode_integer(index, 7) + s.setbyte(0, s.uint8 | 0b10000000) + s end - private def encode_integer(value, prefix_length) mask = (1 << prefix_length) - 1 out = "" @@ -51,9 +91,9 @@ module Plum end def encode_string(str) - huffman_str = Huffman.encode(str) + huffman_str = Huffman.encode(str).force_encoding(__ENCODING__) if huffman_str.bytesize < str.bytesize - lenstr = encode_integer(huffman_str.bytesize, 7).force_encoding(Encoding::BINARY) + lenstr = encode_integer(huffman_str.bytesize, 7) lenstr.setbyte(0, lenstr.uint8(0) | 0b10000000) lenstr << huffman_str else -- cgit v1.2.3