Class: HTTP2::Header::Compressor

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

Overview

Responsible for encoding header key-value pairs using HPACK algorithm.

Instance Method Summary collapse

Constructor Details

#initialize(**options) ⇒ Compressor

Returns a new instance of Compressor.

Parameters:

  • options (Hash)

    encoding options



335
336
337
# File 'lib/http/2/compressor.rb', line 335

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

Instance Method Details

#encode(headers) ⇒ Buffer

Encodes provided list of HTTP headers.

Parameters:

  • headers (Array)

    [[name, value], …]

Returns:



452
453
454
455
456
457
458
459
460
461
462
463
464
465
# File 'lib/http/2/compressor.rb', line 452

def encode(headers)
  buffer = Buffer.new

  # Literal header names MUST be translated to lowercase before
  # encoding and transmission.
  headers.map! {|hk,hv| [hk.downcase, hv] }

  commands = @cc.encode(headers)
  commands.each do |cmd|
    buffer << header(cmd)
  end

  buffer
end

#header(h, buffer = Buffer.new) ⇒ Buffer

Encodes header command with appropriate header representation.

Parameters:

  • h (Hash)

    header command

  • buffer (String) (defaults to: Buffer.new)

Returns:



422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
# File 'lib/http/2/compressor.rb', line 422

def header(h, buffer = Buffer.new)
  rep = HEADREP[h[:type]]

  case h[:type]
  when :indexed
    buffer << integer(h[:name]+1, rep[:prefix])
  when :changetablesize
    buffer << integer(h[:value], rep[:prefix])
  else
    if h[:name].is_a? Integer
      buffer << integer(h[:name]+1, rep[:prefix])
    else
      buffer << integer(0, rep[:prefix])
      buffer << string(h[:name])
    end

    buffer << string(h[:value])
  end

  # set header representation pattern on first byte
  fb = buffer.ord | rep[:pattern]
  buffer.setbyte(0, fb)

  buffer
end

#integer(i, n) ⇒ String

Encodes provided value via integer representation.

If I < 2^N - 1, encode I on N bits
Else
    encode 2^N - 1 on N bits
    I = I - (2^N - 1)
    While I >= 128
         Encode (I % 128 + 128) on 8 bits
         I = I / 128
    encode (I) on 8 bits

Parameters:

  • i (Integer)

    value to encode

  • n (Integer)

    number of available bits

Returns:

  • (String)

    binary string



360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/http/2/compressor.rb', line 360

def integer(i, n)
  limit = 2**n - 1
  return [i].pack('C') if (i < limit)

  bytes = []
  bytes.push limit if !n.zero?

  i -= limit
  while (i >= 128) do
    bytes.push((i % 128) + 128)
    i = i / 128
  end

  bytes.push i
  bytes.pack('C*')
end

#set_table_size(size) ⇒ Object

Set header table size in EncodingContext

Parameters:

  • size (Integer)

    new header table size



341
342
343
# File 'lib/http/2/compressor.rb', line 341

def set_table_size(size)
  @cc.set_table_size(size)
end

#string(str) ⇒ String

Encodes provided value via string literal representation.

  • tools.ietf.org/html/draft-ietf-httpbis-header-compression-09#section-6.2

  • The string length, defined as the number of bytes needed to store its UTF-8 representation, is represented as an integer with a seven bits prefix. If the string length is strictly less than 127, it is represented as one byte.

  • If the bit 7 of the first byte is 1, the string value is represented as a list of Huffman encoded octets (padded with bit 1’s until next octet boundary).

  • If the bit 7 of the first byte is 0, the string value is represented as a list of UTF-8 encoded octets.

@options [:huffman] controls whether to use Huffman encoding:

:never   Do not use Huffman encoding
:always  Always use Huffman encoding
:shorter Use Huffman when the result is strictly shorter

Parameters:

  • str (String)

Returns:

  • (String)

    binary string



397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
# File 'lib/http/2/compressor.rb', line 397

def string(str)
  plain, huffman = nil, nil
  unless @cc.options[:huffman] == :always
    plain = integer(str.bytesize, 7) << str.dup.force_encoding(BINARY)
  end
  unless @cc.options[:huffman] == :never
    huffman = Huffman.new.encode(str)
    huffman = integer(huffman.bytesize, 7) << huffman
    huffman.setbyte(0, huffman.ord | 0x80)
  end
  case @cc.options[:huffman]
  when :always
    huffman
  when :never
    plain
  else
    huffman.bytesize < plain.bytesize ? huffman : plain
  end
end