Module: Hyperion::WebSocket::Builder

Defined in:
lib/hyperion/websocket/frame.rb

Class Method Summary collapse

Class Method Details

.build(opcode:, payload: '', fin: true, mask: false, mask_key: nil, rsv1: false) ⇒ Object

Build a serialized frame ready for ‘socket.write`.

opcode  — Symbol from OPCODES.keys (`:text`, `:binary`, …)
          OR Integer (raw opcode bits).
payload — String. Coerced to ASCII-8BIT internally so callers
          may pass UTF-8 text frames without manual conversion.
fin     — true/false. RFC 6455 §5.4 fragmentation: false on all
          but the final frame of a multi-frame message.
mask    — server frames are unmasked (default).  Clients MUST
          pass mask: true and a 4-byte mask_key.

Control frames (close/ping/pong) MUST have payload <= 125 bytes and MUST have fin: true; the C builder raises ArgumentError if those invariants are violated, which we re-raise as-is.



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/hyperion/websocket/frame.rb', line 183

def self.build(opcode:, payload: '', fin: true, mask: false, mask_key: nil, rsv1: false)
  opcode_int = opcode.is_a?(Symbol) ? OPCODES.fetch(opcode) : Integer(opcode)
  # 2.4-B (S4): skip the redundant `.b` re-encoding when the
  # caller already passes ASCII-8BIT. Servers echoing inbound
  # WebSocket frames (chat, ActionCable, /echo benchmarks) all
  # have binary-encoded payloads at this point — the previous
  # unconditional `.b` allocated a String per send() that just
  # equalled the input.
  bin_payload =
    if payload.is_a?(String)
      payload.encoding == BINARY_ENCODING ? payload : payload.b
    else
      payload.to_s.b
    end

  if mask && mask_key.nil?
    # Caller didn't supply a key — generate one with SecureRandom
    # so client-side tests / scripted clients don't have to.
    require 'securerandom'
    mask_key = SecureRandom.bytes(4)
  end

  ::Hyperion::WebSocket::CFrame.build(
    opcode_int,
    bin_payload,
    fin: fin,
    mask: mask,
    mask_key: mask_key,
    rsv1: rsv1
  )
end