Class: Protocol::HTTP2::Frame
- Inherits:
-
Object
- Object
- Protocol::HTTP2::Frame
- Includes:
- Comparable
- Defined in:
- lib/protocol/http2/frame.rb
Overview
Represents the base class for all HTTP/2 frames. This class provides common functionality for frame parsing, serialization, and manipulation according to RFC 7540.
Direct Known Subclasses
ContinuationFrame, DataFrame, GoawayFrame, HeadersFrame, PingFrame, PriorityUpdateFrame, PushPromiseFrame, ResetStreamFrame, SettingsFrame, WindowUpdateFrame
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- TYPE =
The base class does not have any specific type index:
nil- HEADER_FORMAT =
"CnCCN".freeze
- STREAM_ID_MASK =
0x7fffffff
Instance Attribute Summary collapse
-
#flags ⇒ Object
Returns the value of attribute flags.
-
#length ⇒ Object
The generic frame header uses the following binary representation:.
-
#payload ⇒ Object
Returns the value of attribute payload.
-
#stream_id ⇒ Object
Returns the value of attribute stream_id.
-
#type ⇒ Object
Returns the value of attribute type.
Class Method Summary collapse
-
.parse_header(buffer) ⇒ Object
Decodes common 9-byte header.
Instance Method Summary collapse
-
#<=>(other) ⇒ Object
Compare frames based on their essential properties.
-
#apply(connection) ⇒ Object
Apply the frame to a connection for processing.
-
#clear_flags(mask) ⇒ Object
Clear specific flags on the frame.
-
#connection? ⇒ Boolean
Check if frame is a connection frame: SETTINGS, PING, GOAWAY, and any frame addressed to stream ID = 0.
-
#flag_set?(mask) ⇒ Boolean
Check if specific flags are set on the frame.
-
#header ⇒ String
Generates common 9-byte frame header.
-
#initialize(stream_id = 0, flags = 0, type = self.class::TYPE, length = nil, payload = nil) ⇒ Frame
constructor
A new instance of Frame.
-
#inspect ⇒ Object
Provide a readable representation of the frame for debugging.
-
#pack(payload, maximum_size: nil) ⇒ Object
Pack payload data into the frame.
-
#read(stream, maximum_frame_size = MAXIMUM_ALLOWED_FRAME_SIZE) ⇒ Object
Read the complete frame (header and payload) from a stream.
-
#read_header(stream) ⇒ Object
Read the frame header from a stream.
-
#read_payload(stream) ⇒ Object
Read the frame payload from a stream.
-
#set_flags(mask) ⇒ Object
Set specific flags on the frame.
-
#to_ary ⇒ Object
Convert frame to array representation for comparison.
-
#unpack ⇒ Object
Unpack the frame payload data.
-
#valid_type? ⇒ Boolean
Check if the frame has a valid type identifier.
-
#write(stream) ⇒ Object
Write the complete frame (header and payload) to a stream.
-
#write_header(stream) ⇒ Object
Write the frame header to a stream.
-
#write_payload(stream) ⇒ Object
Write the frame payload to a stream.
Constructor Details
#initialize(stream_id = 0, flags = 0, type = self.class::TYPE, length = nil, payload = nil) ⇒ Frame
Returns a new instance of Frame.
41 42 43 44 45 46 47 |
# File 'lib/protocol/http2/frame.rb', line 41 def initialize(stream_id = 0, flags = 0, type = self.class::TYPE, length = nil, payload = nil) @stream_id = stream_id @flags = flags @type = type @length = length @payload = payload end |
Instance Attribute Details
#flags ⇒ Object
Returns the value of attribute flags.
82 83 84 |
# File 'lib/protocol/http2/frame.rb', line 82 def flags @flags end |
#length ⇒ Object
The generic frame header uses the following binary representation:
----------------------------------------------- | Length (24) | ---------------—————--------------- | Type (8) | Flags (8) | -————----------------——————————-+ |R| Stream Identifier (31) | ==============================================================+ | Frame Payload (0…) … ---------------------------------------------------------------
80 81 82 |
# File 'lib/protocol/http2/frame.rb', line 80 def length @length end |
#payload ⇒ Object
Returns the value of attribute payload.
84 85 86 |
# File 'lib/protocol/http2/frame.rb', line 84 def payload @payload end |
#stream_id ⇒ Object
Returns the value of attribute stream_id.
83 84 85 |
# File 'lib/protocol/http2/frame.rb', line 83 def stream_id @stream_id end |
#type ⇒ Object
Returns the value of attribute type.
81 82 83 |
# File 'lib/protocol/http2/frame.rb', line 81 def type @type end |
Class Method Details
.parse_header(buffer) ⇒ Object
Decodes common 9-byte header.
161 162 163 164 165 166 167 168 169 |
# File 'lib/protocol/http2/frame.rb', line 161 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 |
Instance Method Details
#<=>(other) ⇒ Object
Compare frames based on their essential properties.
58 59 60 |
# File 'lib/protocol/http2/frame.rb', line 58 def <=> other to_ary <=> other.to_ary end |
#apply(connection) ⇒ Object
Apply the frame to a connection for processing.
242 243 244 |
# File 'lib/protocol/http2/frame.rb', line 242 def apply(connection) connection.receive_frame(self) end |
#clear_flags(mask) ⇒ Object
Clear specific flags on the frame.
113 114 115 |
# File 'lib/protocol/http2/frame.rb', line 113 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.
128 129 130 |
# File 'lib/protocol/http2/frame.rb', line 128 def connection? @stream_id.zero? end |
#flag_set?(mask) ⇒ Boolean
Check if specific flags are set on the frame.
120 121 122 |
# File 'lib/protocol/http2/frame.rb', line 120 def flag_set?(mask) @flags & mask != 0 end |
#header ⇒ String
Generates common 9-byte frame header.
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/protocol/http2/frame.rb', line 139 def header unless VALID_LENGTH.include? @length raise ProtocolError, "Invalid frame length: #{@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 ⇒ Object
Provide a readable representation of the frame for debugging.
248 249 250 |
# File 'lib/protocol/http2/frame.rb', line 248 def inspect "\#<#{self.class} stream_id=#{@stream_id} flags=#{@flags} payload=#{self.unpack}>" end |
#pack(payload, maximum_size: nil) ⇒ Object
Pack payload data into the frame.
96 97 98 99 100 101 102 103 |
# File 'lib/protocol/http2/frame.rb', line 96 def pack(payload, maximum_size: nil) @payload = payload @length = payload.bytesize if maximum_size and @length > maximum_size raise ProtocolError, "Frame length bigger than maximum allowed: #{@length} > #{maximum_size}" end end |
#read(stream, maximum_frame_size = MAXIMUM_ALLOWED_FRAME_SIZE) ⇒ Object
Read the complete frame (header and payload) from a stream.
199 200 201 202 203 204 205 206 207 |
# File 'lib/protocol/http2/frame.rb', line 199 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
Read the frame header from a stream.
174 175 176 177 178 179 180 181 |
# File 'lib/protocol/http2/frame.rb', line 174 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) ⇒ Object
Read the frame payload from a stream.
186 187 188 189 190 191 192 |
# File 'lib/protocol/http2/frame.rb', line 186 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) ⇒ Object
Set specific flags on the frame.
107 108 109 |
# File 'lib/protocol/http2/frame.rb', line 107 def set_flags(mask) @flags |= mask end |
#to_ary ⇒ Object
Convert frame to array representation for comparison.
64 65 66 |
# File 'lib/protocol/http2/frame.rb', line 64 def to_ary [@length, @type, @flags, @stream_id, @payload] end |
#unpack ⇒ Object
Unpack the frame payload data.
88 89 90 |
# File 'lib/protocol/http2/frame.rb', line 88 def unpack @payload end |
#valid_type? ⇒ Boolean
Check if the frame has a valid type identifier.
51 52 53 |
# File 'lib/protocol/http2/frame.rb', line 51 def valid_type? @type == self.class::TYPE end |
#write(stream) ⇒ Object
Write the complete frame (header and payload) to a stream.
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/protocol/http2/frame.rb', line 224 def write(stream) # Validate the payload size: if @payload.nil? if @length != 0 raise ProtocolError, "Invalid frame length: #{@length} != 0" end else if @length != @payload.bytesize raise ProtocolError, "Invalid payload size: #{@length} != #{@payload.bytesize}" end end self.write_header(stream) self.write_payload(stream) end |
#write_header(stream) ⇒ Object
Write the frame header to a stream.
211 212 213 |
# File 'lib/protocol/http2/frame.rb', line 211 def write_header(stream) stream.write self.header end |
#write_payload(stream) ⇒ Object
Write the frame payload to a stream.
217 218 219 |
# File 'lib/protocol/http2/frame.rb', line 217 def write_payload(stream) stream.write(@payload) if @payload end |