class Protocol::HTTP2::Frame

Constants

HEADER_FORMAT
LENGTH_HISHIFT

Used for generating 24-bit frame length:

LENGTH_LOMASK
STREAM_ID_MASK
VALID_LENGTH

The absolute maximum bounds for the length field:

VALID_STREAM_ID

Stream Identifier cannot be bigger than this: http2.github.stream/http2-spec/#rfc.section.4.1

Attributes

flags[RW]
length[RW]

The generic frame header uses the following binary representation:

----------------------------------------------- | Length (24) | ---------------—————--------------- | Type (8) | Flags (8) | -————----------------——————————-+ |R| Stream Identifier (31) | +=+=============================================================+ | Frame Payload (0…) … ---------------------------------------------------------------

payload[RW]
stream_id[RW]
type[RW]

Public Class Methods

new(stream_id = 0, flags = 0, type = self.class::TYPE, length = nil, payload = nil) click to toggle source

@param length [Integer] the length of the payload, or nil if the header has not been read yet.

# File lib/protocol/http2/frame.rb, line 50
def initialize(stream_id = 0, flags = 0, type = self.class::TYPE, length = nil, payload = nil)
        @length = length
        @type = type
        @flags = flags
        @stream_id = stream_id
        @payload = payload
end
parse_header(buffer) click to toggle source

Decodes common 9-byte header.

@param buffer [String]

# File lib/protocol/http2/frame.rb, line 150
def self.parse_header(buffer)
        length_hi, length_lo, type, flags, stream_id = buffer.unpack(HEADER_FORMAT)
        length = (length_hi << LENGTH_HISHIFT) | length_lo
        stream_id = stream_id & STREAM_ID_MASK
        
        # puts "parse_header: length=#{length} type=#{type} flags=#{flags} stream_id=#{stream_id}"
        
        return length, type, flags, stream_id
end

Public Instance Methods

<=>(other) click to toggle source
# File lib/protocol/http2/frame.rb, line 62
def <=> other
        to_ary <=> other.to_ary
end
apply(connection) click to toggle source
# File lib/protocol/http2/frame.rb, line 204
def apply(connection)
        connection.receive_frame(self)
end
clear_flags(mask) click to toggle source
# File lib/protocol/http2/frame.rb, line 105
def clear_flags(mask)
        @flags &= ~mask
end
connection?() click to toggle source

Check if frame is a connection frame: SETTINGS, PING, GOAWAY, and any frame addressed to stream ID = 0.

@return [Boolean]

# File lib/protocol/http2/frame.rb, line 117
def connection?
        @stream_id.zero?
end
flag_set?(mask) click to toggle source
# File lib/protocol/http2/frame.rb, line 109
def flag_set?(mask)
        @flags & mask != 0
end
header() click to toggle source

Generates common 9-byte frame header.

@return [String]

# File lib/protocol/http2/frame.rb, line 128
def header
        unless VALID_LENGTH.include? @length
                raise ProtocolError, "Invalid frame size: #{@length.inspect}"
        end
        
        unless VALID_STREAM_ID.include? @stream_id
                raise ProtocolError, "Invalid stream identifier: #{@stream_id.inspect}"
        end
        
        [
                # These are guaranteed correct due to the length check above.
                @length >> LENGTH_HISHIFT,
                @length & LENGTH_LOMASK,
                @type,
                @flags,
                @stream_id
        ].pack(HEADER_FORMAT)
end
inspect() click to toggle source
# File lib/protocol/http2/frame.rb, line 208
def inspect
        "\#<#{self.class} stream_id=#{@stream_id} flags=#{@flags} #{self.unpack}>"
end
pack(payload, maximum_size: nil) click to toggle source
# File lib/protocol/http2/frame.rb, line 92
def pack(payload, maximum_size: nil)
        @payload = payload
        @length = payload.bytesize
        
        if maximum_size and @length > maximum_size
                raise ProtocolError, "Frame length #{@length} bigger than maximum allowed: #{maximum_size}"
        end
end
read(stream, maximum_frame_size = MAXIMUM_ALLOWED_FRAME_SIZE) click to toggle source
# File lib/protocol/http2/frame.rb, line 177
def read(stream, maximum_frame_size = MAXIMUM_ALLOWED_FRAME_SIZE)
        read_header(stream) unless @length
        
        if @length > maximum_frame_size
                raise FrameSizeError, "#{self.class} (type=#{@type}) frame length #{@length} exceeds maximum frame size #{maximum_frame_size}!"
        end
        
        read_payload(stream)
end
read_header(stream) click to toggle source
# File lib/protocol/http2/frame.rb, line 160
def read_header(stream)
        if buffer = stream.read(9) and buffer.bytesize == 9
                @length, @type, @flags, @stream_id = Frame.parse_header(buffer)
                # puts "read_header: #{@length} #{@type} #{@flags} #{@stream_id}"
        else
                raise EOFError, "Could not read frame header!"
        end
end
read_payload(stream) click to toggle source
# File lib/protocol/http2/frame.rb, line 169
def read_payload(stream)
        if payload = stream.read(@length) and payload.bytesize == @length
                @payload = payload
        else
                raise EOFError, "Could not read frame payload!"
        end
end
set_flags(mask) click to toggle source
# File lib/protocol/http2/frame.rb, line 101
def set_flags(mask)
        @flags |= mask
end
to_ary() click to toggle source
# File lib/protocol/http2/frame.rb, line 66
def to_ary
        [@length, @type, @flags, @stream_id, @payload]
end
unpack() click to toggle source
# File lib/protocol/http2/frame.rb, line 88
def unpack
        @payload
end
valid_type?() click to toggle source
# File lib/protocol/http2/frame.rb, line 58
def valid_type?
        @type == self.class::TYPE
end
write(stream) click to toggle source
# File lib/protocol/http2/frame.rb, line 195
def write(stream)
        if @payload and @length != @payload.bytesize
                raise ProtocolError, "Invalid payload size: #{@length} != #{@payload.bytesize}"
        end
        
        self.write_header(stream)
        self.write_payload(stream)
end
write_header(stream) click to toggle source
# File lib/protocol/http2/frame.rb, line 187
def write_header(stream)
        stream.write self.header
end
write_payload(stream) click to toggle source
# File lib/protocol/http2/frame.rb, line 191
def write_payload(stream)
        stream.write(@payload) if @payload
end