Class: HTTP2::Header::Decompressor

Inherits:
Object
  • Object
show all
Includes:
BufferUtils, Error
Defined in:
lib/http/2/header/decompressor.rb,
sig/header/decompressor.rbs

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.

Constant Summary collapse

FORBIDDEN_HEADERS =

Returns:

%w[connection te].freeze

Instance Method Summary collapse

Methods included from BufferUtils

#append_str, #read_str, #read_uint32, #shift_byte

Methods included from Error

#self?.types

Constructor Details

#initialize(settings = Settings.new) ⇒ Decompressor

Returns a new instance of Decompressor.

Parameters:

  • (defaults to: Settings.new)


14
15
16
# File 'lib/http/2/header/decompressor.rb', line 14

def initialize(settings = Settings.new)
  @cc = EncodingContext.new(settings)
end

Instance Method Details

#decode(arg0, arg1) ⇒ Array[header_pair] #decode(arg0) ⇒ Array[header_pair]

Decodes and processes header commands within provided buf.

Overloads:

  • #decode(arg0, arg1) ⇒ Array[header_pair]

    Parameters:

    Returns:

  • #decode(arg0) ⇒ Array[header_pair]

    Parameters:

    Returns:



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/http/2/header/decompressor.rb', line 98

def decode(buf, frame = nil)
  list = [] #: Array[header_pair]
  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 ":status"
          frame[:status] = Integer(value)
        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) ⇒ header_command

Decodes header command from provided buf.

Parameters:

Returns:

Raises:



64
65
66
67
68
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
# File 'lib/http/2/header/decompressor.rb', line 64

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

  header_type, type = HEADREP.find do |_, desc|
    mask = (peek >> desc[:prefix]) << desc[:prefix]
    mask == desc[:pattern]
  end

  raise CompressionError unless header_type && type

  header_name = integer(buf, type[:prefix])

  case header_type
  when :indexed
    raise CompressionError if header_name.zero?

    header_name -= 1

    { type: header_type, name: header_name }
  when :changetablesize
    { type: header_type, name: header_name, value: header_name }
  else
    if header_name.zero?
      header_name = string(buf)
    else
      header_name -= 1
    end
    header_value = string(buf)

    { type: header_type, name: header_name, value: header_value }
  end
end

#integer(buf, n) ⇒ Integer

Decodes integer value from provided buffer.

Parameters:

Returns:



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/http/2/header/decompressor.rb', line 24

def integer(buf, n)
  limit = (1 << n) - 1
  if n.zero?
    i = 0
    consumed = 0
  else
    i = buf.getbyte(0) & limit
    consumed = 1
  end

  if i == limit
    m = 0
    while (byte = buf.getbyte(consumed))
      i += ((byte & 127) << m)
      m += 7
      consumed += 1
      break if byte.nobits?(128)
    end
  end

  read_str(buf, consumed)
  i
end

#string(buf) ⇒ String

Decodes string value from provided buf.

raises CompressionError when input is malformed

Parameters:

Returns:

Raises:



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

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

  huffman = buf.getbyte(0).allbits?(0x80)
  len = integer(buf, 7)
  str = read_str(buf, len)
  raise CompressionError, "string too short" unless str.bytesize == len

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

#table_size=(size) ⇒ void

This method returns an undefined value.

Set dynamic table size in EncodingContext

Parameters:



19
20
21
# File 'lib/http/2/header/decompressor.rb', line 19

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