Class: HTTP2::Header::CompressionContext

Inherits:
Object
  • Object
show all
Includes:
Error
Defined in:
lib/http/2/compressor.rb

Overview

The set of components used to encode or decode a header set form an encoding context: an encoding context contains a header table and a reference set - there is one encoding context for each direction.

Constant Summary collapse

REQ_DEFAULTS =

Default request working set as defined by the spec.

[
  [':scheme'            ,'http' ],
  [':scheme'            ,'https'],
  [':host'              ,''     ],
  [':path'              ,'/'    ],
  [':method'            ,'get'  ],
  ['accept'             ,''     ],
  ['accept-charset'     ,''     ],
  ['accept-encoding'    ,''     ],
  ['accept-language'    ,''     ],
  ['cookie'             ,''     ],
  ['if-modified-since'  ,''     ],
  ['keep-alive'         ,''     ],
  ['user-agent'         ,''     ],
  ['proxy-connection'   ,''     ],
  ['referer'            ,''     ],
  ['accept-datetime'    ,''     ],
  ['authorization'      ,''     ],
  ['allow'              ,''     ],
  ['cache-control'      ,''     ],
  ['connection'         ,''     ],
  ['content-length'     ,''     ],
  ['content-md5'        ,''     ],
  ['content-type'       ,''     ],
  ['date'               ,''     ],
  ['expect'             ,''     ],
  ['from'               ,''     ],
  ['if-match'           ,''     ],
  ['if-none-match'      ,''     ],
  ['if-range'           ,''     ],
  ['if-unmodified-since',''     ],
  ['max-forwards'       ,''     ],
  ['pragma'             ,''     ],
  ['proxy-authorization',''     ],
  ['range'              ,''     ],
  ['te'                 ,''     ],
  ['upgrade'            ,''     ],
  ['via'                ,''     ],
  ['warning'            ,''     ]
]
RESP_DEFAULTS =

Default response working set as defined by the spec.

[
  [':status'                    ,'200'],
  ['age'                        ,''   ],
  ['cache-control'              ,''   ],
  ['content-length'             ,''   ],
  ['content-type'               ,''   ],
  ['date'                       ,''   ],
  ['etag'                       ,''   ],
  ['expires'                    ,''   ],
  ['last-modified'              ,''   ],
  ['server'                     ,''   ],
  ['set-cookie'                 ,''   ],
  ['vary'                       ,''   ],
  ['via'                        ,''   ],
  ['access-control-allow-origin',''   ],
  ['accept-ranges'              ,''   ],
  ['allow'                      ,''   ],
  ['connection'                 ,''   ],
  ['content-disposition'        ,''   ],
  ['content-encoding'           ,''   ],
  ['content-language'           ,''   ],
  ['content-location'           ,''   ],
  ['content-md5'                ,''   ],
  ['content-range'              ,''   ],
  ['link'                       ,''   ],
  ['location'                   ,''   ],
  ['p3p'                        ,''   ],
  ['pragma'                     ,''   ],
  ['proxy-authenticate'         ,''   ],
  ['refresh'                    ,''   ],
  ['retry-after'                ,''   ],
  ['strict-transport-security'  ,''   ],
  ['trailer'                    ,''   ],
  ['transfer-encoding'          ,''   ],
  ['warning'                    ,''   ],
  ['www-authenticate'           ,''   ]
]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type, limit = 4096) ⇒ CompressionContext

Initializes compression context with appropriate client/server defaults and maximum size of the header table.

Parameters:

  • type (Symbol)

    either :request or :response

  • limit (Integer) (defaults to: 4096)

    maximum header table size in bytes



112
113
114
115
116
117
# File 'lib/http/2/compressor.rb', line 112

def initialize(type, limit = 4096)
  @type = type
  @table = (type == :request) ? REQ_DEFAULTS.dup : RESP_DEFAULTS.dup
  @limit = limit
  @workset = []
end

Instance Attribute Details

#tableObject (readonly)

Current table of header key-value pairs.



102
103
104
# File 'lib/http/2/compressor.rb', line 102

def table
  @table
end

#worksetObject (readonly)

Current working set of header key-value pairs.



105
106
107
# File 'lib/http/2/compressor.rb', line 105

def workset
  @workset
end

Instance Method Details

#addcmd(header) ⇒ Object

Emits best available command to encode provided header.

Parameters:

  • header (Hash)


196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/http/2/compressor.rb', line 196

def addcmd(header)
  # check if we have an exact match in header table
  if idx = @table.index(header)
    if !active? idx
      return { name: idx, type: :indexed }
    end
  end

  # check if we have a partial match on header name
  if idx = @table.index {|(k,_)| k == header.first}
    # default to incremental indexing
    cmd = { name: idx, value: header.last, type: :incremental}

    # TODO: implement literal without indexing strategy
    # TODO: implement substitution strategy (if it makes sense)
    # if default? idx
    #   cmd[:type] = :incremental
    # else
    #   cmd[:type] = :substitution
    #   cmd[:index] = idx
    # end

    return cmd
  end

  return { name: header.first, value: header.last, type: :incremental }
end

#process(cmd) ⇒ Object

Performs differential coding based on provided command type.

Parameters:

  • cmd (Hash)


123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/http/2/compressor.rb', line 123

def process(cmd)
  # indexed representation
  if cmd[:type] == :indexed
    # For an indexed representation, the decoder checks whether the index
    # is present in the working set. If true, the corresponding entry is
    # removed from the working set. If several entries correspond to this
    # encoded index, all these entries are removed from the working set.
    # If the index is not present in the working set, it is used to
    # retrieve the corresponding header from the header table, and a new
    # entry is added to the working set representing this header.
    cur = @workset.find_index {|(i,v)| i == cmd[:name]}

    if cur
      @workset.delete_at(cur)
    else
      @workset.push [cmd[:name], @table[cmd[:name]]]
    end

  else
    # For a literal representation, a new entry is added to the working
    # set representing this header. If the literal representation specifies
    # that the header is to be indexed, the header is added accordingly to
    # the header table, and its index is included in the entry in the working
    # set. Otherwise, the entry in the working set contains an undefined index.
    if cmd[:name].is_a? Integer
      k,v = @table[cmd[:name]]

      cmd[:index] ||= cmd[:name]
      cmd[:value] ||= v
      cmd[:name] = k
    end

    newval = [cmd[:name], cmd[:value]]

    if cmd[:type] != :noindex
      size_check cmd

      case cmd[:type]
      when :incremental
        cmd[:index] = @table.size
      when :substitution
        if @table[cmd[:index]].nil?
          raise HeaderException.new("invalid index")
        end
      when :prepend
        @table = [newval] + @table
      end

      @table[cmd[:index]] = newval
    end

    @workset.push [cmd[:index], newval]
  end
end

#removecmd(idx) ⇒ Object

Emits command to remove current index from working set.

Parameters:

  • idx (Integer)


227
228
229
# File 'lib/http/2/compressor.rb', line 227

def removecmd(idx)
  {name: idx, type: :indexed}
end

#update_setsArray

First, upon starting the decoding of a new set of headers, the reference set of headers is interpreted into the working set of headers: for each header in the reference set, an entry is added to the working set, containing the header name, its value, and its current index in the header table.

Returns:

  • (Array)

    current working set



185
186
187
188
189
190
191
# File 'lib/http/2/compressor.rb', line 185

def update_sets
  # new refset is the the workset sans headers not in header table
  refset = @workset.reject {|(i,h)| !@table.include? h}

  # new workset is the refset with index of each header in header table
  @workset = refset.collect {|(i,h)| [@table.find_index(h), h]}
end