Class: NwcRuby::Transport::RelayConnection
- Inherits:
-
Object
- Object
- NwcRuby::Transport::RelayConnection
- Defined in:
- lib/nwc_ruby/transport/relay_connection.rb
Overview
A reliable long-running connection to a Nostr relay.
This is the reliability layer: everything the developer shouldn’t have to think about. It handles:
- RFC 6455 ping every `ping_interval` seconds (keeps middleboxes from
idle-closing the socket; the relay's pong reply is handled by the
protocol layer automatically)
- forced recycle every `recycle_interval` (belt-and-suspenders against
relay bugs or silent connection death)
- capped exponential backoff on reconnect (1s → 2 → 4 → ... → 60s)
- SIGTERM / SIGINT handling for clean Kamal deploys
Usage:
conn = RelayConnection.new(url: "wss://relay.rizful.com")
conn.on_event { |event_hash| ... }
conn.on_open { |c| c.send_req(sub_id: "foo", filters: [...]) }
conn.run! # blocks until stop! or signal
Constant Summary collapse
- DEFAULT_PING_INTERVAL =
15- DEFAULT_RECYCLE_INTERVAL =
300- DEFAULT_MAX_BACKOFF =
60
Instance Attribute Summary collapse
-
#logger ⇒ Object
readonly
Returns the value of attribute logger.
-
#url ⇒ Object
readonly
Returns the value of attribute url.
Instance Method Summary collapse
-
#initialize(url:, ping_interval: DEFAULT_PING_INTERVAL, recycle_interval: DEFAULT_RECYCLE_INTERVAL, max_backoff: DEFAULT_MAX_BACKOFF, poll_interval: nil, logger: default_logger, install_signal_traps: true) ⇒ RelayConnection
constructor
A new instance of RelayConnection.
- #on_error(&block) ⇒ Object
- #on_event(&block) ⇒ Object
- #on_open(&block) ⇒ Object
- #on_poll(&block) ⇒ Object
-
#run! ⇒ Object
Blocks forever, reconnecting as needed, until #stop! is called or SIGTERM / SIGINT is received.
-
#send_close(sub_id) ⇒ Object
Helper: send [“CLOSE”, sub_id].
-
#send_event(event_hash) ⇒ Object
Helper: send [“EVENT”, event_hash].
-
#send_message(message) ⇒ Object
Send raw client->relay message (e.g. REQ, EVENT, CLOSE).
-
#send_req(sub_id:, filters:) ⇒ Object
Helper: send [“REQ”, sub_id, filter1, filter2, …].
- #stop! ⇒ Object
Constructor Details
#initialize(url:, ping_interval: DEFAULT_PING_INTERVAL, recycle_interval: DEFAULT_RECYCLE_INTERVAL, max_backoff: DEFAULT_MAX_BACKOFF, poll_interval: nil, logger: default_logger, install_signal_traps: true) ⇒ RelayConnection
Returns a new instance of RelayConnection.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/nwc_ruby/transport/relay_connection.rb', line 36 def initialize(url:, ping_interval: DEFAULT_PING_INTERVAL, recycle_interval: DEFAULT_RECYCLE_INTERVAL, max_backoff: DEFAULT_MAX_BACKOFF, poll_interval: nil, logger: default_logger, install_signal_traps: true) @url = url @ping_interval = ping_interval @recycle_interval = recycle_interval @max_backoff = max_backoff @poll_interval = poll_interval @logger = logger @event_cb = nil @open_cb = nil @error_cb = nil @poll_cb = nil @stop = false @signal_traps = install_signal_traps @top_task = nil end |
Instance Attribute Details
#logger ⇒ Object (readonly)
Returns the value of attribute logger.
34 35 36 |
# File 'lib/nwc_ruby/transport/relay_connection.rb', line 34 def logger @logger end |
#url ⇒ Object (readonly)
Returns the value of attribute url.
34 35 36 |
# File 'lib/nwc_ruby/transport/relay_connection.rb', line 34 def url @url end |
Instance Method Details
#on_error(&block) ⇒ Object
61 |
# File 'lib/nwc_ruby/transport/relay_connection.rb', line 61 def on_error(&block) = @error_cb = block |
#on_event(&block) ⇒ Object
59 |
# File 'lib/nwc_ruby/transport/relay_connection.rb', line 59 def on_event(&block) = @event_cb = block |
#on_open(&block) ⇒ Object
60 |
# File 'lib/nwc_ruby/transport/relay_connection.rb', line 60 def on_open(&block) = @open_cb = block |
#on_poll(&block) ⇒ Object
62 |
# File 'lib/nwc_ruby/transport/relay_connection.rb', line 62 def on_poll(&block) = @poll_cb = block |
#run! ⇒ Object
Blocks forever, reconnecting as needed, until #stop! is called or SIGTERM / SIGINT is received.
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/nwc_ruby/transport/relay_connection.rb', line 79 def run! install_traps if @signal_traps backoff = 1 Async do |top| @top_task = top until @stop begin run_one_connection(top) backoff = 1 rescue StandardError => e break if @stop @logger.warn("[nwc] connection failed: #{e.class}: #{e.}") @error_cb&.call(e) sleep_seconds = [backoff, @max_backoff].min @logger.info("[nwc] reconnecting in #{sleep_seconds}s") sleep sleep_seconds backoff *= 2 end end ensure @top_task = nil end end |
#send_close(sub_id) ⇒ Object
Helper: send [“CLOSE”, sub_id]
125 126 127 |
# File 'lib/nwc_ruby/transport/relay_connection.rb', line 125 def send_close(sub_id) (['CLOSE', sub_id]) end |
#send_event(event_hash) ⇒ Object
Helper: send [“EVENT”, event_hash]
120 121 122 |
# File 'lib/nwc_ruby/transport/relay_connection.rb', line 120 def send_event(event_hash) (['EVENT', event_hash]) end |
#send_message(message) ⇒ Object
Send raw client->relay message (e.g. REQ, EVENT, CLOSE). Safe to call from within on_open / on_event callbacks.
107 108 109 110 111 112 |
# File 'lib/nwc_ruby/transport/relay_connection.rb', line 107 def () raise TransportError, 'not connected' unless @conn @conn.write(Protocol::WebSocket::TextMessage.generate()) @conn.flush end |
#send_req(sub_id:, filters:) ⇒ Object
Helper: send [“REQ”, sub_id, filter1, filter2, …]
115 116 117 |
# File 'lib/nwc_ruby/transport/relay_connection.rb', line 115 def send_req(sub_id:, filters:) (['REQ', sub_id, *Array(filters)]) end |
#stop! ⇒ Object
64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/nwc_ruby/transport/relay_connection.rb', line 64 def stop! @stop = true # Close the websocket to unblock the read loop. The @stop flag # will cause the run loop to exit on its own. We avoid calling # @top_task.stop here because stop! may be called from a # different thread than the Async reactor. begin @conn&.close rescue StandardError nil end end |