omq 0.6.3 → 0.6.4
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 -0
- data/lib/omq/cli.rb +8 -2
- data/lib/omq/version.rb +1 -1
- data/lib/omq/zmtp/engine.rb +18 -0
- data/lib/omq/zmtp/transport/tcp.rb +35 -20
- data/lib/omq/zmtp.rb +1 -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: 77a41ed459e51dc42f2dcec6976284b7fcbacc2b2a5e946234b76fbf93af74c0
|
|
4
|
+
data.tar.gz: b12af7e7377465ee799c9ff213b2d1b1604bec635fd9d48bfb7af8112fb6a4a9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 44b729c9f970e4fd1c3203dad3cde213464d00d010513d983ca019a577fc123953cc5b6de520b64a821e8fabcfb7778db3a50876416ac19e5ee3b0082804af37
|
|
7
|
+
data.tar.gz: a83241f60938d62edf6241ab6de7719594689cfaa551988d852a39043d297464b7f64405d661ea3c7fdd3d198143f6f263fa930c48490cd73cf45547d566c3f2
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.4 — 2026-03-30
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Dual-stack TCP bind** — `TCP.bind` resolves the hostname via
|
|
8
|
+
`Addrinfo.getaddrinfo` and binds to all returned addresses.
|
|
9
|
+
`tcp://localhost:PORT` now listens on both `127.0.0.1` and `::1`.
|
|
10
|
+
- **Eager DNS validation on connect** — `Engine#connect` resolves TCP
|
|
11
|
+
hostnames upfront via `Addrinfo.getaddrinfo`. Unresolvable hostnames
|
|
12
|
+
raise `Socket::ResolutionError` immediately instead of failing silently
|
|
13
|
+
in the background reconnect loop.
|
|
14
|
+
- **`Socket::ResolutionError` in `CONNECTION_FAILED`** — DNS failures
|
|
15
|
+
during reconnect are now retried with backoff (DNS may recover or
|
|
16
|
+
change), matching libzmq behavior.
|
|
17
|
+
- **CLI catches `SocketDeadError` and `Socket::ResolutionError`** —
|
|
18
|
+
prints the error and exits with code 1 instead of silently exiting 0.
|
|
19
|
+
|
|
20
|
+
### Improved
|
|
21
|
+
|
|
22
|
+
- **CLI endpoint shorthand** — `tcp://:PORT` expands to
|
|
23
|
+
`tcp://localhost:PORT` (loopback, safe default). `tcp://*:PORT` expands
|
|
24
|
+
to `tcp://0.0.0.0:PORT` (all interfaces, explicit opt-in).
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **`tcp://*:PORT` failed on macOS** — `*` is not a resolvable hostname.
|
|
29
|
+
Connects now use `localhost` by default; `*` only expands to `0.0.0.0`
|
|
30
|
+
for explicit all-interface binding.
|
|
31
|
+
- **`Socket` constant resolution inside `OMQ` namespace** — bare `Socket`
|
|
32
|
+
resolved to `OMQ::Socket` instead of `::Socket`, causing `NameError`
|
|
33
|
+
for `Socket::ResolutionError` and `Socket::AI_PASSIVE`.
|
|
34
|
+
|
|
3
35
|
## 0.6.3 — 2026-03-30
|
|
4
36
|
|
|
5
37
|
### Fixed
|
data/lib/omq/cli.rb
CHANGED
|
@@ -268,6 +268,12 @@ module OMQ
|
|
|
268
268
|
rescue IO::TimeoutError, Async::TimeoutError
|
|
269
269
|
$stderr.puts "omq: timeout" unless config.quiet
|
|
270
270
|
exit 2
|
|
271
|
+
rescue OMQ::SocketDeadError => e
|
|
272
|
+
$stderr.puts "omq: #{e.cause.class}: #{e.cause.message}"
|
|
273
|
+
exit 1
|
|
274
|
+
rescue ::Socket::ResolutionError => e
|
|
275
|
+
$stderr.puts "omq: #{e.message}"
|
|
276
|
+
exit 1
|
|
271
277
|
end
|
|
272
278
|
end
|
|
273
279
|
|
|
@@ -415,9 +421,9 @@ module OMQ
|
|
|
415
421
|
|
|
416
422
|
opts[:type_name] = type_name.downcase
|
|
417
423
|
|
|
418
|
-
normalize = ->(url) { url.sub(%r{\Atcp://:}, "tcp
|
|
419
|
-
opts[:connects].map!(&normalize)
|
|
424
|
+
normalize = ->(url) { url.sub(%r{\Atcp://\*:}, "tcp://0.0.0.0:").sub(%r{\Atcp://:}, "tcp://localhost:") }
|
|
420
425
|
opts[:binds].map!(&normalize)
|
|
426
|
+
opts[:connects].map!(&normalize)
|
|
421
427
|
opts[:endpoints].map! { |ep| Endpoint.new(normalize.call(ep.url), ep.bind?) }
|
|
422
428
|
|
|
423
429
|
opts
|
data/lib/omq/version.rb
CHANGED
data/lib/omq/zmtp/engine.rb
CHANGED
|
@@ -89,6 +89,7 @@ module OMQ
|
|
|
89
89
|
#
|
|
90
90
|
def connect(endpoint)
|
|
91
91
|
capture_parent_task
|
|
92
|
+
validate_endpoint!(endpoint)
|
|
92
93
|
@connected_endpoints << endpoint
|
|
93
94
|
if endpoint.start_with?("inproc://")
|
|
94
95
|
# Inproc connect is synchronous and instant
|
|
@@ -348,6 +349,7 @@ module OMQ
|
|
|
348
349
|
wrapped
|
|
349
350
|
end
|
|
350
351
|
@routing.recv_queue.enqueue(nil) rescue nil
|
|
352
|
+
@peer_connected.resolve(nil) rescue nil
|
|
351
353
|
end
|
|
352
354
|
|
|
353
355
|
|
|
@@ -462,10 +464,26 @@ module OMQ
|
|
|
462
464
|
delay = ri.is_a?(Range) ? ri.begin : ri if delay == 0
|
|
463
465
|
end
|
|
464
466
|
end
|
|
467
|
+
rescue Async::Stop
|
|
468
|
+
# normal shutdown
|
|
469
|
+
rescue => error
|
|
470
|
+
signal_fatal_error(error)
|
|
465
471
|
end
|
|
466
472
|
end
|
|
467
473
|
|
|
468
474
|
|
|
475
|
+
# Eagerly validates TCP hostnames so resolution errors fail
|
|
476
|
+
# on connect, not silently in the background reconnect loop.
|
|
477
|
+
# Reconnects still re-resolve (DNS may change), and transient
|
|
478
|
+
# resolution failures during reconnect are retried with backoff.
|
|
479
|
+
#
|
|
480
|
+
def validate_endpoint!(endpoint)
|
|
481
|
+
return unless endpoint.start_with?("tcp://")
|
|
482
|
+
host = URI.parse(endpoint.sub("tcp://", "http://")).hostname
|
|
483
|
+
Addrinfo.getaddrinfo(host, nil, nil, :STREAM) if host
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
|
|
469
487
|
def transport_for(endpoint)
|
|
470
488
|
case endpoint
|
|
471
489
|
when /\Atcp:\/\// then Transport::TCP
|
|
@@ -20,23 +20,38 @@ module OMQ
|
|
|
20
20
|
def bind(endpoint, engine)
|
|
21
21
|
host, port = parse_endpoint(endpoint)
|
|
22
22
|
host = "0.0.0.0" if host == "*"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
|
|
24
|
+
addrs = Addrinfo.getaddrinfo(host, port, nil, :STREAM, nil, ::Socket::AI_PASSIVE)
|
|
25
|
+
raise ::Socket::ResolutionError, "no addresses for #{host}" if addrs.empty?
|
|
26
|
+
|
|
27
|
+
servers = []
|
|
28
|
+
accept_tasks = []
|
|
29
|
+
actual_port = nil
|
|
30
|
+
|
|
31
|
+
addrs.each do |addr|
|
|
32
|
+
server = TCPServer.new(addr.ip_address, actual_port || port)
|
|
33
|
+
actual_port ||= server.local_address.ip_port
|
|
34
|
+
servers << server
|
|
35
|
+
|
|
36
|
+
ip = addr.ip_address
|
|
37
|
+
host_part = ip.include?(":") ? "[#{ip}]" : ip
|
|
38
|
+
resolved = "tcp://#{host_part}:#{actual_port}"
|
|
39
|
+
|
|
40
|
+
accept_tasks << Reactor.spawn_pump(annotation: "tcp accept #{resolved}") do
|
|
41
|
+
loop do
|
|
42
|
+
client = server.accept
|
|
43
|
+
Async::Task.current.defer_stop do
|
|
44
|
+
engine.handle_accepted(IO::Stream::Buffered.wrap(client), endpoint: resolved)
|
|
45
|
+
end
|
|
33
46
|
end
|
|
47
|
+
rescue IOError
|
|
48
|
+
# server closed
|
|
34
49
|
end
|
|
35
|
-
rescue IOError
|
|
36
|
-
# server closed
|
|
37
50
|
end
|
|
38
51
|
|
|
39
|
-
|
|
52
|
+
host_part = host.include?(":") ? "[#{host}]" : host
|
|
53
|
+
resolved = "tcp://#{host_part}:#{actual_port}"
|
|
54
|
+
Listener.new(resolved, servers, accept_tasks, actual_port)
|
|
40
55
|
end
|
|
41
56
|
|
|
42
57
|
# Connects to a TCP endpoint.
|
|
@@ -81,19 +96,19 @@ module OMQ
|
|
|
81
96
|
# @param accept_task [#stop] the accept loop handle
|
|
82
97
|
# @param port [Integer] bound port number
|
|
83
98
|
#
|
|
84
|
-
def initialize(endpoint,
|
|
85
|
-
@endpoint
|
|
86
|
-
@
|
|
87
|
-
@
|
|
88
|
-
@port
|
|
99
|
+
def initialize(endpoint, servers, accept_tasks, port)
|
|
100
|
+
@endpoint = endpoint
|
|
101
|
+
@servers = servers
|
|
102
|
+
@accept_tasks = accept_tasks
|
|
103
|
+
@port = port
|
|
89
104
|
end
|
|
90
105
|
|
|
91
106
|
|
|
92
107
|
# Stops the listener.
|
|
93
108
|
#
|
|
94
109
|
def stop
|
|
95
|
-
@
|
|
96
|
-
@
|
|
110
|
+
@accept_tasks.each(&:stop)
|
|
111
|
+
@servers.each { |s| s.close rescue nil }
|
|
97
112
|
end
|
|
98
113
|
end
|
|
99
114
|
end
|
data/lib/omq/zmtp.rb
CHANGED