Class: HTTP2::Header::Decompressor

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

Methods included from BufferUtils

#read_str, #read_uint32, #shift_byte

Constructor Details

#initialize(options = {}) ⇒ Decompressor

Returns a new instance of Decompressor.

Parameters:

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

    decoding options. Only :table_size is effective.



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

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 (HTTP2::Frame, nil) (defaults to: nil)

Returns:

  • (Array)

    +[[name, value], …]



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

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 ":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) ⇒ Hash

Decodes header command from provided buffer.

Parameters:

  • buf (Buffer)

Returns:

  • (Hash)

    command

Raises:



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

def header(buf)
  peek = buf.getbyte(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)


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

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

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

      break if byte.nobits?(128)
    end
  end

  i
end

#string(buf) ⇒ String

Decodes string value from provided buffer.

Parameters:

  • buf (String)

Returns:

  • (String)

    UTF-8 encoded string

Raises:



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

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.new.decode(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



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

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