omq 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -2
- data/lib/omq/socket.rb +0 -6
- data/lib/omq/version.rb +1 -1
- data/lib/omq/zmtp/connection.rb +1 -1
- data/lib/omq/zmtp/engine.rb +19 -34
- data/lib/omq/zmtp/options.rb +2 -12
- data/lib/omq/zmtp/routing/fan_out.rb +2 -2
- data/lib/omq/zmtp/routing/pair.rb +1 -1
- data/lib/omq/zmtp/routing/rep.rb +1 -1
- data/lib/omq/zmtp/routing/round_robin.rb +1 -1
- data/lib/omq/zmtp/transport/ipc.rb +4 -2
- data/lib/omq/zmtp/transport/tcp.rb +5 -8
- data/lib/omq/zmtp.rb +21 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fb02b0e08db21f4d0db4bb376a35759ca709ef80d175a306179c3710e8048e9d
|
|
4
|
+
data.tar.gz: 41bfa63e9868e6a5d10c97e9751ebe227f8937be734d9f9bc5c618619cb1906c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cf95700a644923f1598944501e10775de5ba0cfd277c375f3701c93e09a5e1928e495acea2e09eb5224f87143e55526f851852c2ba67d92409a5e0da62ec5266
|
|
7
|
+
data.tar.gz: 9b299ca752fee2e1cba919d65bd7a377494e8067d111f27ac53c8dc2a35b3c9c3d0547386e38417656f58b746ea3dda6ea5b8292e1d9f9230736150561aad1b8
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0 — 2026-03-26
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- `mechanism` option now holds the mechanism instance directly
|
|
8
|
+
(`Mechanism::Null.new` by default). For CURVE, use
|
|
9
|
+
`OMQ::Curve.server(pub, sec)` or `OMQ::Curve.client(pub, sec, server_key: k)`.
|
|
10
|
+
- Removed `curve_server`, `curve_server_key`, `curve_public_key`,
|
|
11
|
+
`curve_secret_key`, `curve_authenticator` socket options
|
|
12
|
+
|
|
13
|
+
## 0.1.1 — 2026-03-26
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Handle `Errno::EPIPE`, `Errno::ECONNRESET`, `Errno::ECONNABORTED`,
|
|
18
|
+
`Errno::EHOSTUNREACH`, `Errno::ENETUNREACH`, `Errno::ENOTCONN`, and
|
|
19
|
+
`IO::Stream::ConnectionResetError` in accept loops, connect, reconnect,
|
|
20
|
+
and recv/send pumps — prevents unhandled exceptions when peers disconnect
|
|
21
|
+
during handshake or become unreachable
|
|
22
|
+
- Use `TCPSocket.new` instead of `Socket.tcp` for reliable cross-host
|
|
23
|
+
connections with io-stream
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- TCP/IPC `#connect` is now non-blocking — returns immediately and
|
|
28
|
+
establishes the connection in the background, like libzmq
|
|
29
|
+
- Consolidated connection error handling via `ZMTP::CONNECTION_LOST` and
|
|
30
|
+
`ZMTP::CONNECTION_FAILED` constants
|
|
31
|
+
- Removed `connect_timeout` option (no longer needed since connect is
|
|
32
|
+
non-blocking)
|
|
33
|
+
|
|
3
34
|
## 0.1.0 — 2026-03-25
|
|
4
35
|
|
|
5
36
|
Initial release. Pure Ruby implementation of ZMTP 3.1 (ZeroMQ) using Async.
|
|
@@ -25,6 +56,5 @@ Initial release. Pure Ruby implementation of ZMTP 3.1 (ZeroMQ) using Async.
|
|
|
25
56
|
- Per-socket send/receive HWM (high-water mark)
|
|
26
57
|
- Linger on close (drain send queue before closing)
|
|
27
58
|
- `max_message_size` enforcement
|
|
28
|
-
- `connect_timeout` for TCP
|
|
29
59
|
- Works inside Async reactors or standalone (shared IO thread)
|
|
30
|
-
- Optional CURVE encryption via the
|
|
60
|
+
- Optional CURVE encryption via the [omq-curve](https://github.com/paddor/omq-curve) gem
|
data/lib/omq/socket.rb
CHANGED
|
@@ -30,13 +30,7 @@ module OMQ
|
|
|
30
30
|
heartbeat_ttl heartbeat_ttl=
|
|
31
31
|
heartbeat_timeout heartbeat_timeout=
|
|
32
32
|
max_message_size max_message_size=
|
|
33
|
-
connect_timeout connect_timeout=
|
|
34
33
|
mechanism mechanism=
|
|
35
|
-
curve_server curve_server=
|
|
36
|
-
curve_server_key curve_server_key=
|
|
37
|
-
curve_public_key curve_public_key=
|
|
38
|
-
curve_secret_key curve_secret_key=
|
|
39
|
-
curve_authenticator curve_authenticator=
|
|
40
34
|
].each do |method|
|
|
41
35
|
define_method(method) { |*args| @options.public_send(method, *args) }
|
|
42
36
|
end
|
data/lib/omq/version.rb
CHANGED
data/lib/omq/zmtp/connection.rb
CHANGED
data/lib/omq/zmtp/engine.rb
CHANGED
|
@@ -68,11 +68,14 @@ module OMQ
|
|
|
68
68
|
#
|
|
69
69
|
def connect(endpoint)
|
|
70
70
|
@connected_endpoints << endpoint
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
if endpoint.start_with?("inproc://")
|
|
72
|
+
# Inproc connect is synchronous and instant
|
|
73
|
+
transport = transport_for(endpoint)
|
|
74
|
+
transport.connect(endpoint, self)
|
|
75
|
+
else
|
|
76
|
+
# TCP/IPC connect in background — never blocks the caller
|
|
77
|
+
schedule_reconnect(endpoint, delay: 0)
|
|
78
|
+
end
|
|
76
79
|
end
|
|
77
80
|
|
|
78
81
|
# Disconnects from an endpoint. Closes connections to that endpoint
|
|
@@ -183,7 +186,7 @@ module OMQ
|
|
|
183
186
|
msg = transform ? transform.call(msg) : msg
|
|
184
187
|
recv_queue.enqueue(msg)
|
|
185
188
|
end
|
|
186
|
-
rescue
|
|
189
|
+
rescue *CONNECTION_LOST
|
|
187
190
|
connection_lost(conn)
|
|
188
191
|
end
|
|
189
192
|
end
|
|
@@ -257,7 +260,7 @@ module OMQ
|
|
|
257
260
|
socket_type: @socket_type.to_s,
|
|
258
261
|
identity: @options.identity,
|
|
259
262
|
as_server: as_server,
|
|
260
|
-
mechanism:
|
|
263
|
+
mechanism: @options.mechanism,
|
|
261
264
|
heartbeat_interval: @options.heartbeat_interval,
|
|
262
265
|
heartbeat_ttl: @options.heartbeat_ttl,
|
|
263
266
|
heartbeat_timeout: @options.heartbeat_timeout,
|
|
@@ -268,58 +271,40 @@ module OMQ
|
|
|
268
271
|
@connections << conn
|
|
269
272
|
@connection_endpoints[conn] = endpoint if endpoint
|
|
270
273
|
@routing.connection_added(conn)
|
|
271
|
-
rescue ProtocolError,
|
|
274
|
+
rescue ProtocolError, *CONNECTION_LOST
|
|
272
275
|
conn&.close
|
|
273
276
|
raise
|
|
274
277
|
end
|
|
275
278
|
|
|
276
|
-
def schedule_reconnect(endpoint)
|
|
279
|
+
def schedule_reconnect(endpoint, delay: nil)
|
|
277
280
|
ri = @options.reconnect_interval
|
|
278
281
|
if ri.is_a?(Range)
|
|
279
|
-
delay
|
|
282
|
+
delay ||= ri.begin
|
|
280
283
|
max_delay = ri.end
|
|
281
284
|
else
|
|
282
|
-
delay
|
|
285
|
+
delay ||= ri
|
|
283
286
|
max_delay = nil
|
|
284
287
|
end
|
|
285
288
|
|
|
286
289
|
@tasks << Reactor.spawn_pump do
|
|
287
290
|
loop do
|
|
288
291
|
break if @closed
|
|
289
|
-
sleep delay
|
|
292
|
+
sleep delay if delay > 0
|
|
290
293
|
break if @closed
|
|
291
294
|
begin
|
|
292
295
|
transport = transport_for(endpoint)
|
|
293
296
|
transport.connect(endpoint, self)
|
|
294
|
-
break #
|
|
295
|
-
rescue
|
|
297
|
+
break # connected successfully
|
|
298
|
+
rescue *CONNECTION_LOST, *CONNECTION_FAILED, ProtocolError
|
|
296
299
|
delay = [delay * 2, max_delay].min if max_delay
|
|
300
|
+
# After first attempt with delay: 0, use the configured interval
|
|
301
|
+
delay = ri.is_a?(Range) ? ri.begin : ri if delay == 0
|
|
297
302
|
end
|
|
298
303
|
end
|
|
299
304
|
end
|
|
300
305
|
end
|
|
301
306
|
|
|
302
307
|
|
|
303
|
-
def build_mechanism
|
|
304
|
-
case @options.mechanism
|
|
305
|
-
when :null
|
|
306
|
-
Mechanism::Null.new
|
|
307
|
-
when :curve
|
|
308
|
-
unless defined?(Mechanism::Curve)
|
|
309
|
-
raise LoadError, "require 'omq-curve' to use CURVE security"
|
|
310
|
-
end
|
|
311
|
-
Mechanism::Curve.new(
|
|
312
|
-
server_key: @options.curve_server_key,
|
|
313
|
-
public_key: @options.curve_public_key,
|
|
314
|
-
secret_key: @options.curve_secret_key,
|
|
315
|
-
as_server: @options.curve_server,
|
|
316
|
-
authenticator: @options.curve_authenticator,
|
|
317
|
-
)
|
|
318
|
-
else
|
|
319
|
-
raise ArgumentError, "unknown mechanism: #{@options.mechanism}"
|
|
320
|
-
end
|
|
321
|
-
end
|
|
322
|
-
|
|
323
308
|
def transport_for(endpoint)
|
|
324
309
|
case endpoint
|
|
325
310
|
when /\Atcp:\/\// then Transport::TCP
|
data/lib/omq/zmtp/options.rb
CHANGED
|
@@ -25,13 +25,7 @@ module OMQ
|
|
|
25
25
|
@heartbeat_ttl = nil # seconds, nil = use heartbeat_interval
|
|
26
26
|
@heartbeat_timeout = nil # seconds, nil = use heartbeat_interval
|
|
27
27
|
@max_message_size = nil # bytes, nil = unlimited
|
|
28
|
-
@
|
|
29
|
-
@mechanism = :null # :null or :curve
|
|
30
|
-
@curve_server = false
|
|
31
|
-
@curve_server_key = nil # 32-byte binary (server's permanent public key)
|
|
32
|
-
@curve_public_key = nil # 32-byte binary (our permanent public key)
|
|
33
|
-
@curve_secret_key = nil # 32-byte binary (our permanent secret key)
|
|
34
|
-
@curve_authenticator = nil # nil = allow all, Set = allowlist, #call = custom
|
|
28
|
+
@mechanism = Mechanism::Null.new
|
|
35
29
|
end
|
|
36
30
|
|
|
37
31
|
attr_accessor :send_hwm, :recv_hwm,
|
|
@@ -41,11 +35,7 @@ module OMQ
|
|
|
41
35
|
:reconnect_interval,
|
|
42
36
|
:heartbeat_interval, :heartbeat_ttl, :heartbeat_timeout,
|
|
43
37
|
:max_message_size,
|
|
44
|
-
:
|
|
45
|
-
:mechanism,
|
|
46
|
-
:curve_server, :curve_server_key,
|
|
47
|
-
:curve_public_key, :curve_secret_key,
|
|
48
|
-
:curve_authenticator
|
|
38
|
+
:mechanism
|
|
49
39
|
|
|
50
40
|
alias_method :router_mandatory?, :router_mandatory
|
|
51
41
|
alias_method :recv_timeout, :read_timeout
|
|
@@ -60,7 +60,7 @@ module OMQ
|
|
|
60
60
|
next unless subscribed?(conn, topic)
|
|
61
61
|
begin
|
|
62
62
|
conn.send_message(parts)
|
|
63
|
-
rescue
|
|
63
|
+
rescue *ZMTP::CONNECTION_LOST
|
|
64
64
|
# connection dead — will be cleaned up
|
|
65
65
|
end
|
|
66
66
|
end
|
|
@@ -79,7 +79,7 @@ module OMQ
|
|
|
79
79
|
when "CANCEL" then on_cancel(conn, cmd.data)
|
|
80
80
|
end
|
|
81
81
|
end
|
|
82
|
-
rescue
|
|
82
|
+
rescue *ZMTP::CONNECTION_LOST
|
|
83
83
|
@engine.connection_lost(conn)
|
|
84
84
|
end
|
|
85
85
|
end
|
data/lib/omq/zmtp/routing/rep.rb
CHANGED
|
@@ -33,9 +33,11 @@ module OMQ
|
|
|
33
33
|
client = server.accept
|
|
34
34
|
Reactor.run do
|
|
35
35
|
engine.handle_accepted(IO::Stream::Buffered.wrap(client, minimum_write_size: 0), endpoint: endpoint)
|
|
36
|
+
rescue ProtocolError, *ZMTP::CONNECTION_LOST
|
|
37
|
+
# peer disconnected during handshake
|
|
36
38
|
rescue => e
|
|
37
|
-
client
|
|
38
|
-
raise
|
|
39
|
+
client&.close rescue nil
|
|
40
|
+
raise
|
|
39
41
|
end
|
|
40
42
|
end
|
|
41
43
|
rescue IOError
|
|
@@ -30,9 +30,11 @@ module OMQ
|
|
|
30
30
|
client = server.accept
|
|
31
31
|
Reactor.run do
|
|
32
32
|
engine.handle_accepted(IO::Stream::Buffered.wrap(client, minimum_write_size: 0), endpoint: resolved)
|
|
33
|
+
rescue ProtocolError, *ZMTP::CONNECTION_LOST
|
|
34
|
+
# peer disconnected during handshake
|
|
33
35
|
rescue => e
|
|
34
|
-
client
|
|
35
|
-
raise
|
|
36
|
+
client&.close rescue nil
|
|
37
|
+
raise
|
|
36
38
|
end
|
|
37
39
|
end
|
|
38
40
|
rescue IOError
|
|
@@ -50,12 +52,7 @@ module OMQ
|
|
|
50
52
|
#
|
|
51
53
|
def connect(endpoint, engine)
|
|
52
54
|
host, port = parse_endpoint(endpoint)
|
|
53
|
-
|
|
54
|
-
sock = if timeout
|
|
55
|
-
::Socket.tcp(host, port, connect_timeout: timeout)
|
|
56
|
-
else
|
|
57
|
-
TCPSocket.new(host, port)
|
|
58
|
-
end
|
|
55
|
+
sock = TCPSocket.new(host, port)
|
|
59
56
|
engine.handle_connected(IO::Stream::Buffered.wrap(sock, minimum_write_size: 0), endpoint: endpoint)
|
|
60
57
|
end
|
|
61
58
|
|
data/lib/omq/zmtp.rb
CHANGED
|
@@ -7,6 +7,27 @@ module OMQ
|
|
|
7
7
|
# strategies. They are not part of the public API.
|
|
8
8
|
#
|
|
9
9
|
module ZMTP
|
|
10
|
+
require "io/stream"
|
|
11
|
+
|
|
12
|
+
# Errors raised when a peer disconnects or resets the connection.
|
|
13
|
+
CONNECTION_LOST = [
|
|
14
|
+
EOFError,
|
|
15
|
+
IOError,
|
|
16
|
+
Errno::EPIPE,
|
|
17
|
+
Errno::ECONNRESET,
|
|
18
|
+
Errno::ECONNABORTED,
|
|
19
|
+
Errno::ENOTCONN,
|
|
20
|
+
IO::Stream::ConnectionResetError,
|
|
21
|
+
].freeze
|
|
22
|
+
|
|
23
|
+
# Errors raised when a peer cannot be reached.
|
|
24
|
+
CONNECTION_FAILED = [
|
|
25
|
+
Errno::ECONNREFUSED,
|
|
26
|
+
Errno::ENOENT,
|
|
27
|
+
Errno::ETIMEDOUT,
|
|
28
|
+
Errno::EHOSTUNREACH,
|
|
29
|
+
Errno::ENETUNREACH,
|
|
30
|
+
].freeze
|
|
10
31
|
end
|
|
11
32
|
end
|
|
12
33
|
|