Class: PumaPlus::WSEvent

Inherits:
Object
  • Object
show all
Defined in:
lib/puma_plus/ws_event.rb

Overview

Convenience wrapper over the WebSocket keys in a Rack env.

A WebSocket event arrives as an ordinary Rack call with REQUEST_METHOD set to WEBSOCKET, so existing routing and middleware work unchanged. This just saves the app from string-keyed env lookups.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env) ⇒ WSEvent

Returns a new instance of WSEvent.



12
13
14
# File 'lib/puma_plus/ws_event.rb', line 12

def initialize(env)
  @env = env
end

Class Method Details

.upgrade(mode: :flexible, topics: [], meta: {}, transport: nil) ⇒ Object

Build the 101 response that asks Go to take over a connection.

PumaPlus::WSEvent.upgrade(mode: :sticky, topics: ["room:42"])


68
69
70
71
72
73
74
75
76
# File 'lib/puma_plus/ws_event.rb', line 68

def self.upgrade(mode: :flexible, topics: [], meta: {}, transport: nil)
  headers = { "puma-plus-websocket" => mode.to_s }
  headers["puma-plus-transport"] = transport.to_s if transport
  headers["puma-plus-websocket-topic"] = Array(topics).map(&:to_s) unless Array(topics).empty?
  unless meta.empty?
    headers["puma-plus-websocket-meta"] = meta.map { |k, v| "#{k}=#{v}" }
  end
  [101, headers, []]
end

.websocket?(env) ⇒ Boolean

Returns:

  • (Boolean)


10
# File 'lib/puma_plus/ws_event.rb', line 10

def self.websocket?(env) = env["REQUEST_METHOD"] == "WEBSOCKET"

Instance Method Details

#binary?Boolean

Returns:

  • (Boolean)


42
# File 'lib/puma_plus/ws_event.rb', line 42

def binary? = @env["puma_plus.ws.opcode"] == "binary"

#close?Boolean

Returns:

  • (Boolean)


20
# File 'lib/puma_plus/ws_event.rb', line 20

def close?    = kind == "close"

#close_codeObject



44
# File 'lib/puma_plus/ws_event.rb', line 44

def close_code   = @env["puma_plus.ws.close_code"]&.to_i

#close_reasonObject



45
# File 'lib/puma_plus/ws_event.rb', line 45

def close_reason = @env["puma_plus.ws.close_reason"]

#conn_idObject



39
# File 'lib/puma_plus/ws_event.rb', line 39

def conn_id = @env["puma_plus.ws.conn_id"]

#dataObject

The message payload.



48
49
50
# File 'lib/puma_plus/ws_event.rb', line 48

def data
  @data ||= (@env["rack.input"]&.read || "")
end

#datagram?Boolean

An unreliable datagram. A distinct event kind from #message? on purpose: this payload may have been dropped, duplicated or reordered, and handling it identically to a guaranteed message is usually a bug.

Returns:

  • (Boolean)


25
# File 'lib/puma_plus/ws_event.rb', line 25

def datagram? = kind == "datagram"

#kindObject

open | message | datagram | stream_open | stream_close | close



17
# File 'lib/puma_plus/ws_event.rb', line 17

def kind      = @env["puma_plus.ws.event"]

#message?Boolean

Returns:

  • (Boolean)


19
# File 'lib/puma_plus/ws_event.rb', line 19

def message?  = kind == "message"

#metaObject

Metadata captured at upgrade time and echoed on every event, so a flexible handler knows who it is talking to without a lookup.



54
55
56
57
58
# File 'lib/puma_plus/ws_event.rb', line 54

def meta
  @meta ||= @env.each_with_object({}) do |(k, v), h|
    h[k.delete_prefix("puma_plus.ws.meta.")] = v if k.start_with?("puma_plus.ws.meta.")
  end
end

#modeObject



40
# File 'lib/puma_plus/ws_event.rb', line 40

def mode    = @env["puma_plus.ws.mode"]

#open?Boolean

Returns:

  • (Boolean)


18
# File 'lib/puma_plus/ws_event.rb', line 18

def open?     = kind == "open"

#queue_timeObject

Queue time for this event, in seconds. WebSocket messages are queued and measured exactly like HTTP requests, which is what lets them drive autoscaling.



63
# File 'lib/puma_plus/ws_event.rb', line 63

def queue_time = @env["puma.request_queue_time"]

#sticky?Boolean

Returns:

  • (Boolean)


41
# File 'lib/puma_plus/ws_event.rb', line 41

def sticky? = mode == "sticky"

#stream_close?Boolean

Returns:

  • (Boolean)


28
# File 'lib/puma_plus/ws_event.rb', line 28

def stream_close? = kind == "stream_close"

#stream_idObject

Which stream a WebTransport message arrived on. nil for WebSocket, whose single channel needs no name.



37
# File 'lib/puma_plus/ws_event.rb', line 37

def stream_id = @env["puma_plus.ws.stream_id"]

#stream_open?Boolean

Returns:

  • (Boolean)


27
# File 'lib/puma_plus/ws_event.rb', line 27

def stream_open?  = kind == "stream_open"

#transportObject

"websocket" or "webtransport". Usually irrelevant -- that is the point of a shared hub -- but needed before reaching for a datagram.



32
# File 'lib/puma_plus/ws_event.rb', line 32

def transport   = @env["puma_plus.ws.transport"]

#webtransport?Boolean

Returns:

  • (Boolean)


33
# File 'lib/puma_plus/ws_event.rb', line 33

def webtransport? = transport == "webtransport"