Module: Hyperion::WebSocket::Parser
- Defined in:
- lib/hyperion/websocket/frame.rb
Class Method Summary collapse
-
.parse(buf, offset = 0) ⇒ Object
Parse one frame out of ‘buf` starting at `offset`.
-
.parse_with_cursor(buf, offset = 0) ⇒ Object
Lower-level variant exposing the raw 7-tuple from the C parser AND the cursor advance the caller should apply.
Class Method Details
.parse(buf, offset = 0) ⇒ Object
Parse one frame out of ‘buf` starting at `offset`. Does NOT mutate or consume `buf` — the caller is responsible for advancing its cursor by `frame_total_len` (see `parse_with_cursor` below).
Returns:
* a Hyperion::WebSocket::Frame on success
* `:incomplete` if `buf[offset..]` doesn't yet hold a full frame
Raises Hyperion::WebSocket::ProtocolError on malformed frames (RSV bits set without a negotiated extension, unknown opcode, control frame > 125 bytes, fragmented control frame, 64-bit length with high bit set).
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/hyperion/websocket/frame.rb', line 90 def self.parse(buf, offset = 0) result = ::Hyperion::WebSocket::CFrame.parse(buf, offset) return result if result == :incomplete raise ProtocolError, 'malformed WebSocket frame' if result == :error fin, opcode, payload_len, masked, mask_key, payload_offset, _frame_total_len, rsv1 = result opcode_sym = OPCODE_NAMES[opcode] || raise(ProtocolError, "unknown opcode 0x#{opcode.to_s(16)}") payload = if payload_len.zero? # 2.4-B (S5): share one frozen empty binary String across # every empty-payload frame instead of allocating # `(+'').b` (= 2 strings) per parse. EMPTY_BIN_PAYLOAD else slice = buf.byteslice(payload_offset, payload_len) # 2.4-B (S5): when the input @inbuf is ASCII-8BIT (which # WS::Connection guarantees: see `@inbuf = String.new( # capacity:, encoding: ASCII_8BIT)`), `slice.b` is a no-op # that allocates a redundant String clone. Skip when the # source slice already has the right encoding. if masked ::Hyperion::WebSocket::CFrame.unmask(slice, mask_key) elsif slice.encoding == BINARY_ENCODING slice else slice.b end end Frame.new(fin: fin, opcode: opcode_sym, payload: payload, rsv1: rsv1 ? true : false) end |
.parse_with_cursor(buf, offset = 0) ⇒ Object
Lower-level variant exposing the raw 7-tuple from the C parser AND the cursor advance the caller should apply. WS-1’s read loop uses this form to drain multiple frames out of a single buffer in one pass without re-parsing the leading bytes.
Returns ‘[Frame, frame_total_len]` on success, `:incomplete` if not enough bytes have arrived yet. Raises ProtocolError on malformed input.
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 |
# File 'lib/hyperion/websocket/frame.rb', line 134 def self.parse_with_cursor(buf, offset = 0) result = ::Hyperion::WebSocket::CFrame.parse(buf, offset) return result if result == :incomplete raise ProtocolError, 'malformed WebSocket frame' if result == :error fin, opcode, payload_len, masked, mask_key, payload_offset, frame_total_len, rsv1 = result opcode_sym = OPCODE_NAMES[opcode] || raise(ProtocolError, "unknown opcode 0x#{opcode.to_s(16)}") payload = if payload_len.zero? # 2.4-B (S5): share one frozen empty binary String. See # parse() above for the rationale. EMPTY_BIN_PAYLOAD else slice = buf.byteslice(payload_offset, payload_len) if masked ::Hyperion::WebSocket::CFrame.unmask(slice, mask_key) elsif slice.encoding == BINARY_ENCODING slice else slice.b end end [ Frame.new(fin: fin, opcode: opcode_sym, payload: payload, rsv1: rsv1 ? true : false), frame_total_len ] end |