omq 0.3.2 → 0.4.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.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -0
  3. data/exe/omqcat +50 -24
  4. data/lib/omq/version.rb +1 -1
  5. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 27a1d700594261a36d3212b00ff84e15be3053a32db07a04a48aa0eb52fec902
4
- data.tar.gz: 2797e4863af5cfcde9fe446c79929705e6ab17656ba016532b54d6c6b57564b9
3
+ metadata.gz: d49a1c7caa7a1548615285ae294341b31833fd02bb60a7fa7cbfc0ed5c707d79
4
+ data.tar.gz: db36537c5dab13faa9cbdbe611c282cbf9dd96726e88145f80c3aaa7bf3df8a5
5
5
  SHA512:
6
- metadata.gz: a116b039dc996a0099bbb86e8c138fd71f03051bd23c142b653e1c8e932e541245476bed51a47183ecb3723f9f86b30f36b6e3956b77494f8d77c924354f39ad
7
- data.tar.gz: bdd7a62bb68390c942ebb1517981ca69d0efab007f3be7d3149fcccb525ad9b2cb279689ba2ec640db0f6057d6d7219e804074bb5ba4eb4d8339517bf6de5805
6
+ metadata.gz: 3e3a9e5a0b9ee3857a2946e7468ea2ca30c87544fa75965c79b1ea905c3a8d969aeb94b49282acd502d2ffbb6a0db5b2952f7a18a17e90363f1c5ed13cfbecd4
7
+ data.tar.gz: 1cb5f13312278d55684f22c420f17287a922633eebdfd143336415aafdc725dd7a38e23b5f9387cc55bf28acc0b125840c5770ef9112556b97b7c3cca8578ee6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0 — 2026-03-27
4
+
5
+ ### Added (omqcat)
6
+
7
+ - `--curve-server` flag — generates ephemeral keypair, prints
8
+ `OMQ_SERVER_KEY=...` to stderr for easy copy-paste
9
+ - `--curve-server-key KEY` flag — CURVE client mode from the CLI
10
+ - `--echo` flag for REP — explicit echo mode
11
+ - REP reads stdin/`-F` as reply source (one line per reply, exits at EOF)
12
+ - REP without a reply source now aborts with a helpful error message
13
+
14
+ ### Changed
15
+
16
+ - CURVE env vars renamed: `OMQ_SERVER_KEY`, `OMQ_SERVER_PUBLIC`,
17
+ `OMQ_SERVER_SECRET` (was `SERVER_KEY`, `SERVER_PUBLIC`, `SERVER_SECRET`)
18
+ - REP with `--echo`/`-D`/`-e` serves forever by default (like a server).
19
+ Use `-n 1` for one-shot, `-n` to limit exchanges. Stdin/`-F` replies
20
+ naturally terminate at EOF.
21
+
3
22
  ## 0.3.2 — 2026-03-26
4
23
 
5
24
  ### Improved
data/exe/omqcat CHANGED
@@ -30,7 +30,7 @@ opts = {
30
30
  identity: nil,
31
31
  target: nil,
32
32
  interval: nil,
33
- count: 0,
33
+ count: nil,
34
34
  delay: nil,
35
35
  recv_timeout: nil,
36
36
  send_timeout: nil,
@@ -48,7 +48,8 @@ parser = OptionParser.new do |o|
48
48
  o.on("-c", "--connect URL", "Connect to endpoint (repeatable)") { |v| opts[:connects] << v }
49
49
  o.on("-b", "--bind URL", "Bind to endpoint (repeatable)") { |v| opts[:binds] << v }
50
50
 
51
- o.separator "\nData source:"
51
+ o.separator "\nData source (REP: reply source):"
52
+ o.on( "--echo", "Echo received messages back (REP)") { opts[:echo] = true }
52
53
  o.on("-D", "--data DATA", "Message data (literal string)") { |v| opts[:data] = v }
53
54
  o.on("-F", "--file FILE", "Read message from file (- = stdin)") { |v| opts[:file] = v }
54
55
 
@@ -80,6 +81,11 @@ parser = OptionParser.new do |o|
80
81
  o.on("-e", "--eval EXPR", "Eval Ruby for each message ($F = parts)") { |v| opts[:expr] = v }
81
82
  o.on("-r", "--require LIB", "Require a Ruby library (repeatable)") { |v| require v }
82
83
 
84
+ o.separator "\nCURVE encryption (requires omq-curve gem):"
85
+ o.on("--curve-server", "Enable CURVE as server (generates keypair)") { opts[:curve_server] = true }
86
+ o.on("--curve-server-key KEY", "Enable CURVE as client (server's Z85 public key)") { |v| opts[:curve_server_key] = v }
87
+ o.separator " Env vars: OMQ_SERVER_KEY (client), OMQ_SERVER_PUBLIC + OMQ_SERVER_SECRET (server)"
88
+
83
89
  o.separator "\nOther:"
84
90
  o.on("-v", "--verbose", "Print connection events to stderr") { opts[:verbose] = true }
85
91
  o.on("-q", "--quiet", "Suppress message output") { opts[:quiet] = true }
@@ -216,21 +222,31 @@ end
216
222
 
217
223
  # ── CURVE setup ─────────────────────────────────────────────────────
218
224
 
219
- def setup_curve(sock)
220
- if ENV["SERVER_KEY"]
225
+ def setup_curve(sock, opts)
226
+ server_key_z85 = opts[:curve_server_key] || ENV["OMQ_SERVER_KEY"]
227
+ server_mode = opts[:curve_server] || (ENV["OMQ_SERVER_PUBLIC"] && ENV["OMQ_SERVER_SECRET"])
228
+
229
+ if server_key_z85
230
+ # Client mode
221
231
  require "omq/curve"
222
- server_key = OMQ::Z85.decode(ENV["SERVER_KEY"])
232
+ server_key = OMQ::Z85.decode(server_key_z85)
223
233
  client_key = RbNaCl::PrivateKey.generate
224
234
  sock.mechanism = OMQ::Curve.client(
225
235
  client_key.public_key.to_s, client_key.to_s, server_key: server_key
226
236
  )
227
- $stderr.puts "CURVE client mode" if $VERBOSE
228
- elsif ENV["SERVER_PUBLIC"] && ENV["SERVER_SECRET"]
237
+ elsif server_mode
238
+ # Server mode
229
239
  require "omq/curve"
230
- server_pub = OMQ::Z85.decode(ENV["SERVER_PUBLIC"])
231
- server_sec = OMQ::Z85.decode(ENV["SERVER_SECRET"])
240
+ if ENV["OMQ_SERVER_PUBLIC"] && ENV["OMQ_SERVER_SECRET"]
241
+ server_pub = OMQ::Z85.decode(ENV["OMQ_SERVER_PUBLIC"])
242
+ server_sec = OMQ::Z85.decode(ENV["OMQ_SERVER_SECRET"])
243
+ else
244
+ key = RbNaCl::PrivateKey.generate
245
+ server_pub = key.public_key.to_s
246
+ server_sec = key.to_s
247
+ end
232
248
  sock.mechanism = OMQ::Curve.server(server_pub, server_sec)
233
- $stderr.puts "SERVER_KEY=#{ENV["SERVER_PUBLIC"]}"
249
+ $stderr.puts "OMQ_SERVER_KEY='#{OMQ::Z85.encode(server_pub)}'"
234
250
  end
235
251
  rescue LoadError
236
252
  abort "omq-curve gem required for CURVE encryption: gem install omq-curve"
@@ -281,6 +297,10 @@ def eval_expr(parts, sock, opts)
281
297
  end
282
298
  end
283
299
 
300
+ def count_reached?(i, opts)
301
+ opts[:count] && opts[:count] > 0 && i >= opts[:count]
302
+ end
303
+
284
304
  def output(parts, opts)
285
305
  return if opts[:quiet]
286
306
  parts = [""] if parts.nil?
@@ -304,7 +324,7 @@ def send_loop(sock, opts)
304
324
  sleep(opts[:delay]) if opts[:delay] && i == 0
305
325
  send_msg(sock, parts, opts) if parts
306
326
  i += 1
307
- break if opts[:count] > 0 && i >= opts[:count]
327
+ break if count_reached?(i, opts)
308
328
  if opts[:interval]
309
329
  sleep(opts[:interval])
310
330
  else
@@ -319,7 +339,7 @@ def send_loop(sock, opts)
319
339
  sleep(opts[:delay]) if opts[:delay] && i == 0
320
340
  send_msg(sock, parts, opts) if parts
321
341
  i += 1
322
- break if opts[:count] > 0 && i >= opts[:count]
342
+ break if count_reached?(i, opts)
323
343
  sleep(opts[:interval]) if opts[:interval]
324
344
  end
325
345
  end
@@ -332,7 +352,7 @@ def recv_loop(sock, opts)
332
352
  parts = eval_expr(parts, sock, opts)
333
353
  output(parts, opts)
334
354
  i += 1
335
- break if opts[:count] > 0 && i >= opts[:count]
355
+ break if count_reached?(i, opts)
336
356
  end
337
357
  end
338
358
 
@@ -347,7 +367,7 @@ def req_loop(sock, opts)
347
367
  reply = eval_expr(reply, sock, opts)
348
368
  output(reply, opts)
349
369
  i += 1
350
- break if opts[:count] > 0 && i >= opts[:count]
370
+ break if count_reached?(i, opts)
351
371
  if opts[:interval]
352
372
  sleep(opts[:interval])
353
373
  elsif !opts[:data] && !opts[:file]
@@ -366,13 +386,19 @@ def rep_loop(sock, opts)
366
386
  reply = eval_expr(msg, sock, opts)
367
387
  output(reply, opts)
368
388
  send_msg(sock, reply || [""], opts)
369
- else
389
+ elsif opts[:echo]
390
+ output(msg, opts)
391
+ send_msg(sock, msg, opts)
392
+ elsif opts[:data] || opts[:file] || !$stdin.tty?
393
+ reply = read_next(opts)
394
+ break unless reply # EOF on stdin/-F
370
395
  output(msg, opts)
371
- reply = opts[:data] ? parse_input(opts[:data] + "\n", opts[:format]) : msg
372
396
  send_msg(sock, reply, opts)
397
+ else
398
+ abort "REP needs a reply source: --echo, --data, --file, -e, or stdin pipe"
373
399
  end
374
400
  i += 1
375
- break if opts[:count] > 0 && i >= opts[:count]
401
+ break if count_reached?(i, opts)
376
402
  end
377
403
  end
378
404
 
@@ -384,7 +410,7 @@ def pair_loop(sock, opts, task)
384
410
  parts = eval_expr(parts, sock, opts)
385
411
  output(parts, opts)
386
412
  i += 1
387
- break if opts[:count] > 0 && i >= opts[:count]
413
+ break if count_reached?(i, opts)
388
414
  end
389
415
  end
390
416
 
@@ -397,13 +423,13 @@ def pair_loop(sock, opts, task)
397
423
  sleep(opts[:delay]) if opts[:delay] && i == 0
398
424
  send_msg(sock, parts, opts) if parts
399
425
  i += 1
400
- break if opts[:count] > 0 && i >= opts[:count]
426
+ break if count_reached?(i, opts)
401
427
  break if (opts[:data] || opts[:file]) && !opts[:interval]
402
428
  sleep(opts[:interval]) if opts[:interval]
403
429
  end
404
430
  end
405
431
 
406
- if opts[:count] > 0
432
+ if opts[:count] && opts[:count] > 0
407
433
  receiver.wait
408
434
  sender.stop
409
435
  else
@@ -425,7 +451,7 @@ def router_loop(sock, opts, task)
425
451
  result = eval_expr([id_display, *parts], sock, opts)
426
452
  output(result, opts)
427
453
  i += 1
428
- break if opts[:count] > 0 && i >= opts[:count]
454
+ break if count_reached?(i, opts)
429
455
  end
430
456
  end
431
457
 
@@ -440,7 +466,7 @@ def router_loop(sock, opts, task)
440
466
  end
441
467
  send_msg(sock, parts, opts)
442
468
  i += 1
443
- break if opts[:count] > 0 && i >= opts[:count]
469
+ break if count_reached?(i, opts)
444
470
  break if (opts[:data] || opts[:file]) && !opts[:interval]
445
471
  sleep(opts[:interval]) if opts[:interval]
446
472
  end
@@ -448,7 +474,7 @@ def router_loop(sock, opts, task)
448
474
 
449
475
  # If count is set, the receiver will exit when count is reached.
450
476
  # Otherwise, wait for Ctrl-C.
451
- if opts[:count] > 0
477
+ if opts[:count] && opts[:count] > 0
452
478
  receiver.wait
453
479
  sender.stop
454
480
  else
@@ -473,7 +499,7 @@ Async do |task|
473
499
  sock.send_timeout = opts[:send_timeout] if opts[:send_timeout]
474
500
  sock.identity = opts[:identity] if opts[:identity]
475
501
 
476
- setup_curve(sock)
502
+ setup_curve(sock, opts)
477
503
 
478
504
  opts[:binds].each do |url|
479
505
  sock.bind(url)
data/lib/omq/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OMQ
4
- VERSION = "0.3.2"
4
+ VERSION = "0.4.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omq
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrik Wenger