Class: HTTP2Next::Header::Decompressor

Inherits:
Object
  • Object
show all
Includes:
Error
Defined in:
lib/http/2/next/header/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.

Examples:

server_role = Decompressor.new(:request)
client_role = Decompressor.new(:response)

Constant Summary collapse

FORBIDDEN_HEADERS =
%w[connection te].freeze

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Decompressor

Returns a new instance of Decompressor.

Parameters:

  • options (Hash) (defaults to: {})

    decoding options. Only :table_size is effective.



16
17
18
# File 'lib/http/2/next/header/decompressor.rb', line 16

def initialize(options = {})
  @cc = EncodingContext.new(options)
end

Instance Method Details

#decode(buf, frame = nil) ⇒ Array

Decodes and processes header commands within provided buffer.

Parameters:

  • buf (Buffer)
  • frame (HTTP2Next::Frame, nil) (defaults to: nil)

Returns:

  • (Array)

    +[[name, value], …]



108
109
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
# File 'lib/http/2/next/header/decompressor.rb', line 108

def decode(buf, frame = nil)
  list = []
  decoding_pseudo_headers = true
  @cc.listen_on_table do
    until buf.empty?
      field, value = @cc.process(header(buf))
      next if field.nil?

      is_pseudo_header = field.start_with? ":"
      if !decoding_pseudo_headers && is_pseudo_header
        raise ProtocolError, "one or more pseudo headers encountered after regular headers"
      end

      decoding_pseudo_headers = is_pseudo_header
      raise ProtocolError, "invalid header received: #{field}" if FORBIDDEN_HEADERS.include?(field)

      if frame
        case field
        when ":method"
          frame[:method] = value
        when "content-length"
          frame[:content_length] = Integer(value)
        when "trailer"
          (frame[:trailer] ||= []) << value
        end
      end
      list << [field, value]
    end
  end
  list
end

#header(buf) ⇒ Hash

Decodes header command from provided buffer.

Parameters:

Returns:

  • (Hash)

    command

Raises:



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/http/2/next/header/decompressor.rb', line 69

def header(buf)
  peek = buf.readbyte(0)

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

  raise CompressionError unless header[:type]

  header[:name] = integer(buf, type[:prefix])

  case header[:type]
  when :indexed
    raise CompressionError if (header[:name]).zero?

    header[:name] -= 1
  when :changetablesize
    header[:value] = header[:name]
  else
    if (header[:name]).zero?
      header[:name] = string(buf)
    else
      header[:name] -= 1
    end
    header[:value] = string(buf)
  end

  header
end

#integer(buf, n) ⇒ Integer

Decodes integer value from provided buffer.

Parameters:

  • buf (String)
  • n (Integer)

    number of available bits

Returns:

  • (Integer)


31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/http/2/next/header/decompressor.rb', line 31

def integer(buf, n)
  limit = 2**n - 1
  i = !n.zero? ? (buf.getbyte & limit) : 0

  m = 0
  if i == limit
    while (byte = buf.getbyte)
      i += ((byte & 127) << m)
      m += 7

      break if (byte & 128).zero?
    end
  end

  i
end

#string(buf) ⇒ String

Decodes string value from provided buffer.

Parameters:

  • buf (String)

Returns:

  • (String)

    UTF-8 encoded string

Raises:



53
54
55
56
57
58
59
60
61
62
63
# File 'lib/http/2/next/header/decompressor.rb', line 53

def string(buf)
  raise CompressionError, "invalid header block fragment" if buf.empty?

  huffman = (buf.readbyte(0) & 0x80) == 0x80
  len = integer(buf, 7)
  str = buf.read(len)
  raise CompressionError, "string too short" unless str.bytesize == len

  str = Huffman.new.decode(Buffer.new(str)) if huffman
  str.force_encoding(Encoding::UTF_8)
end

#table_size=(size) ⇒ Object

Set dynamic table size in EncodingContext

Parameters:

  • size (Integer)

    new dynamic table size



22
23
24
# File 'lib/http/2/next/header/decompressor.rb', line 22

def table_size=(size)
  @cc.table_size = size
end