Class: HTTP::Protocol::HTTP2::Frame

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/http/protocol/http2/frame.rb

Constant Summary collapse

VALID_STREAM_ID =

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

0..0x7fffffff
VALID_LENGTH =

The absolute maximum bounds for the length field:

0..0xffffff
LENGTH_HISHIFT =

Used for generating 24-bit frame length:

16
LENGTH_LOMASK =
0xFFFF
HEADER_FORMAT =
'CnCCN'.freeze
STREAM_ID_MASK =
0x7fffffff

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(stream_id = 0, flags = 0, type = self.class.const_get(:TYPE), length = nil, payload = nil) ⇒ Frame

Returns a new instance of Frame.

Parameters:

  • length (Integer) (defaults to: nil)

    the length of the payload, or nil if the header has not been read yet.



50
51
52
53
54
55
56
# File 'lib/http/protocol/http2/frame.rb', line 50

def initialize(stream_id = 0, flags = 0, type = self.class.const_get(:TYPE), length = nil, payload = nil)
	@length = length
	@type = type
	@flags = flags
	@stream_id = stream_id
	@payload = payload
end

Instance Attribute Details

#flagsObject

Returns the value of attribute flags.



80
81
82
# File 'lib/http/protocol/http2/frame.rb', line 80

def flags
  @flags
end

#lengthObject

The generic frame header uses the following binary representation:

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



78
79
80
# File 'lib/http/protocol/http2/frame.rb', line 78

def length
  @length
end

#payloadObject

Returns the value of attribute payload.



82
83
84
# File 'lib/http/protocol/http2/frame.rb', line 82

def payload
  @payload
end

#stream_idObject

Returns the value of attribute stream_id.



81
82
83
# File 'lib/http/protocol/http2/frame.rb', line 81

def stream_id
  @stream_id
end

#typeObject

Returns the value of attribute type.



79
80
81
# File 'lib/http/protocol/http2/frame.rb', line 79

def type
  @type
end

Class Method Details

.parse_header(buffer) ⇒ Object

Decodes common 9-byte header.

Parameters:

  • buffer (String)


146
147
148
149
150
151
152
# File 'lib/http/protocol/http2/frame.rb', line 146

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
	
	return length, type, flags, stream_id
end

Instance Method Details

#<=>(other) ⇒ Object



58
59
60
# File 'lib/http/protocol/http2/frame.rb', line 58

def <=> other
	to_ary <=> other.to_ary
end

#apply(connection) ⇒ Object



189
190
191
# File 'lib/http/protocol/http2/frame.rb', line 189

def apply(connection)
	connection.receive_frame(self)
end

#clear_flags(mask) ⇒ Object



101
102
103
# File 'lib/http/protocol/http2/frame.rb', line 101

def clear_flags(mask)
	@flags &= ~mask
end

#connection?Boolean

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

Returns:

  • (Boolean)


113
114
115
# File 'lib/http/protocol/http2/frame.rb', line 113

def connection?
	@stream_id.zero?
end

#flag_set?(mask) ⇒ Boolean

Returns:

  • (Boolean)


105
106
107
# File 'lib/http/protocol/http2/frame.rb', line 105

def flag_set?(mask)
	@flags & mask != 0
end

#headerString

Generates common 9-byte frame header.

Returns:

  • (String)


124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/http/protocol/http2/frame.rb', line 124

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

#pack(payload, maximum_size: nil) ⇒ Object



88
89
90
91
92
93
94
95
# File 'lib/http/protocol/http2/frame.rb', line 88

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) ⇒ Object



162
163
164
165
166
167
168
169
170
# File 'lib/http/protocol/http2/frame.rb', line 162

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) ⇒ Object



154
155
156
# File 'lib/http/protocol/http2/frame.rb', line 154

def read_header(stream)
	@length, @type, @flags, @stream_id = Frame.parse_header(stream.read(9))
end

#read_payload(stream) ⇒ Object



158
159
160
# File 'lib/http/protocol/http2/frame.rb', line 158

def read_payload(stream)
	@payload = stream.read(@length)
end

#set_flags(mask) ⇒ Object



97
98
99
# File 'lib/http/protocol/http2/frame.rb', line 97

def set_flags(mask)
	@flags |= mask
end

#to_aryObject



62
63
64
# File 'lib/http/protocol/http2/frame.rb', line 62

def to_ary
	[@length, @type, @flags, @stream_id, @payload]
end

#unpackObject



84
85
86
# File 'lib/http/protocol/http2/frame.rb', line 84

def unpack
	@payload
end

#write(stream) ⇒ Object



180
181
182
183
184
185
186
187
# File 'lib/http/protocol/http2/frame.rb', line 180

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) ⇒ Object



172
173
174
# File 'lib/http/protocol/http2/frame.rb', line 172

def write_header(stream)
	stream.write self.header
end

#write_payload(stream) ⇒ Object



176
177
178
# File 'lib/http/protocol/http2/frame.rb', line 176

def write_payload(stream)
	stream.write(@payload) if @payload
end