nng 0.1.0 → 1.0.1

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.
data/ext/rbnng/stats.c ADDED
@@ -0,0 +1,106 @@
1
+ #include "rbnng.h"
2
+
3
+ /* ── Recursive stat tree → Ruby Hash ──────────────────────────── */
4
+
5
+ static VALUE unit_sym(int unit)
6
+ {
7
+ switch (unit) {
8
+ case NNG_UNIT_BYTES: return ID2SYM(rb_intern("bytes"));
9
+ case NNG_UNIT_MESSAGES: return ID2SYM(rb_intern("messages"));
10
+ case NNG_UNIT_MILLIS: return ID2SYM(rb_intern("millis"));
11
+ case NNG_UNIT_EVENTS: return ID2SYM(rb_intern("events"));
12
+ default: return Qnil;
13
+ }
14
+ }
15
+
16
+ static VALUE stat_value(nng_stat *s)
17
+ {
18
+ switch (nng_stat_type(s)) {
19
+ case NNG_STAT_COUNTER:
20
+ case NNG_STAT_LEVEL:
21
+ case NNG_STAT_ID:
22
+ return ULL2NUM(nng_stat_value(s));
23
+ case NNG_STAT_STRING: {
24
+ const char *v = nng_stat_string(s);
25
+ return v ? rb_str_new_cstr(v) : Qnil;
26
+ }
27
+ case NNG_STAT_BOOLEAN:
28
+ return nng_stat_bool(s) ? Qtrue : Qfalse;
29
+ default:
30
+ return Qnil;
31
+ }
32
+ }
33
+
34
+ static VALUE stat_to_hash(nng_stat *s)
35
+ {
36
+ VALUE hash = rb_hash_new();
37
+
38
+ for (nng_stat *c = nng_stat_child(s); c; c = nng_stat_next(c)) {
39
+ const char *name = nng_stat_name(c);
40
+ VALUE key = ID2SYM(rb_intern(name));
41
+
42
+ if (nng_stat_type(c) == NNG_STAT_SCOPE) {
43
+ /* recurse into scopes */
44
+ rb_hash_aset(hash, key, stat_to_hash(c));
45
+ } else {
46
+ /* leaf: {value:, desc:, unit:} or just value for simple cases */
47
+ VALUE entry = rb_hash_new();
48
+ rb_hash_aset(entry, ID2SYM(rb_intern("value")), stat_value(c));
49
+
50
+ const char *desc = nng_stat_desc(c);
51
+ if (desc && desc[0])
52
+ rb_hash_aset(entry, ID2SYM(rb_intern("desc")), rb_str_new_cstr(desc));
53
+
54
+ VALUE u = unit_sym(nng_stat_unit(c));
55
+ if (!NIL_P(u))
56
+ rb_hash_aset(entry, ID2SYM(rb_intern("unit")), u);
57
+
58
+ rb_hash_aset(hash, key, entry);
59
+ }
60
+ }
61
+
62
+ return hash;
63
+ }
64
+
65
+ /* ── NNG.stats → Hash ─────────────────────────────────────────── */
66
+
67
+ static VALUE
68
+ nng_rb_stats(VALUE self)
69
+ {
70
+ nng_stat *root;
71
+ int rv = nng_stats_get(&root);
72
+ if (rv != 0)
73
+ raise_nng_error(rv);
74
+
75
+ VALUE result = stat_to_hash(root);
76
+ nng_stats_free(root);
77
+ return result;
78
+ }
79
+
80
+ /* ── NNG.stats_for(socket) → Hash ─────────────────────────────── */
81
+
82
+ static VALUE
83
+ nng_rb_stats_for(VALUE self, VALUE socket_obj)
84
+ {
85
+ rbnng_socket_t *s;
86
+ TypedData_Get_Struct(socket_obj, rbnng_socket_t, &rbnng_socket_type, s);
87
+
88
+ nng_stat *root;
89
+ int rv = nng_stats_get(&root);
90
+ if (rv != 0)
91
+ raise_nng_error(rv);
92
+
93
+ nng_stat *ss = nng_stat_find_socket(root, s->socket);
94
+ VALUE result = ss ? stat_to_hash(ss) : rb_hash_new();
95
+ nng_stats_free(root);
96
+ return result;
97
+ }
98
+
99
+ /* ── Init ──────────────────────────────────────────────────────── */
100
+
101
+ void
102
+ rbnng_stats_init(VALUE nng_module)
103
+ {
104
+ rb_define_singleton_method(nng_module, "stats", nng_rb_stats, 0);
105
+ rb_define_singleton_method(nng_module, "stats_for", nng_rb_stats_for, 1);
106
+ }
data/ext/rbnng/tls.c ADDED
@@ -0,0 +1,127 @@
1
+ #include "rbnng.h"
2
+ #include <nng/supplemental/tls/tls.h>
3
+ #include <nng/transport/tls/tls.h>
4
+
5
+ /*
6
+ * rbnng_tls_listen / rbnng_tls_dial — two-step listener/dialer with TLS config.
7
+ *
8
+ * Called from socket_listen_tls / socket_dial_tls in socket.c.
9
+ * TLS kwargs are parsed in Ruby; C receives PEM strings directly.
10
+ */
11
+
12
+ /* ── Helpers ───────────────────────────────────────────────────────── */
13
+
14
+ static nng_tls_config *
15
+ build_tls_config(nng_tls_mode mode, VALUE cert_pem, VALUE key_pem,
16
+ VALUE ca_pem, int verify, VALUE server_name)
17
+ {
18
+ nng_tls_config *cfg;
19
+ int rv = nng_tls_config_alloc(&cfg, mode);
20
+ if (rv != 0)
21
+ raise_nng_error(rv);
22
+
23
+ /* Own certificate + key (server identity, or client mTLS) */
24
+ if (!NIL_P(cert_pem) && !NIL_P(key_pem)) {
25
+ rv = nng_tls_config_own_cert(cfg,
26
+ StringValueCStr(cert_pem),
27
+ StringValueCStr(key_pem),
28
+ NULL);
29
+ if (rv != 0) {
30
+ nng_tls_config_free(cfg);
31
+ raise_nng_error(rv);
32
+ }
33
+ }
34
+
35
+ /* CA chain for peer verification */
36
+ if (!NIL_P(ca_pem)) {
37
+ rv = nng_tls_config_ca_chain(cfg, StringValueCStr(ca_pem), NULL);
38
+ if (rv != 0) {
39
+ nng_tls_config_free(cfg);
40
+ raise_nng_error(rv);
41
+ }
42
+ }
43
+
44
+ /* Auth mode */
45
+ nng_tls_auth_mode auth = verify ? NNG_TLS_AUTH_MODE_REQUIRED
46
+ : NNG_TLS_AUTH_MODE_NONE;
47
+ rv = nng_tls_config_auth_mode(cfg, auth);
48
+ if (rv != 0) {
49
+ nng_tls_config_free(cfg);
50
+ raise_nng_error(rv);
51
+ }
52
+
53
+ /* Server name (SNI / certificate matching) — client only */
54
+ if (!NIL_P(server_name)) {
55
+ rv = nng_tls_config_server_name(cfg, StringValueCStr(server_name));
56
+ if (rv != 0) {
57
+ nng_tls_config_free(cfg);
58
+ raise_nng_error(rv);
59
+ }
60
+ }
61
+
62
+ return cfg;
63
+ }
64
+
65
+ /* ── Public C API (called from socket.c) ──────────────────────────── */
66
+
67
+ void
68
+ rbnng_tls_listen(nng_socket sock, const char *url,
69
+ VALUE cert_pem, VALUE key_pem, VALUE ca_pem,
70
+ int verify, VALUE server_name)
71
+ {
72
+ nng_tls_config *cfg = build_tls_config(NNG_TLS_MODE_SERVER,
73
+ cert_pem, key_pem, ca_pem,
74
+ verify, server_name);
75
+
76
+ nng_listener listener;
77
+ int rv = nng_listener_create(&listener, sock, url);
78
+ if (rv != 0) {
79
+ nng_tls_config_free(cfg);
80
+ raise_nng_error(rv);
81
+ }
82
+
83
+ rv = nng_listener_set_ptr(listener, NNG_OPT_TLS_CONFIG, cfg);
84
+ if (rv != 0) {
85
+ nng_tls_config_free(cfg);
86
+ raise_nng_error(rv);
87
+ }
88
+
89
+ rv = nng_listener_start(listener, 0);
90
+ if (rv != 0) {
91
+ nng_tls_config_free(cfg);
92
+ raise_nng_error(rv);
93
+ }
94
+
95
+ nng_tls_config_free(cfg);
96
+ }
97
+
98
+ void
99
+ rbnng_tls_dial(nng_socket sock, const char *url,
100
+ VALUE cert_pem, VALUE key_pem, VALUE ca_pem,
101
+ int verify, VALUE server_name)
102
+ {
103
+ nng_tls_config *cfg = build_tls_config(NNG_TLS_MODE_CLIENT,
104
+ cert_pem, key_pem, ca_pem,
105
+ verify, server_name);
106
+
107
+ nng_dialer dialer;
108
+ int rv = nng_dialer_create(&dialer, sock, url);
109
+ if (rv != 0) {
110
+ nng_tls_config_free(cfg);
111
+ raise_nng_error(rv);
112
+ }
113
+
114
+ rv = nng_dialer_set_ptr(dialer, NNG_OPT_TLS_CONFIG, cfg);
115
+ if (rv != 0) {
116
+ nng_tls_config_free(cfg);
117
+ raise_nng_error(rv);
118
+ }
119
+
120
+ rv = nng_dialer_start(dialer, 0);
121
+ if (rv != 0) {
122
+ nng_tls_config_free(cfg);
123
+ raise_nng_error(rv);
124
+ }
125
+
126
+ nng_tls_config_free(cfg);
127
+ }
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ module Socket
5
+ class Base
6
+ alias_method :send_msg, :send
7
+ alias_method :get_msg, :receive
8
+ alias_method :_listen, :listen
9
+ alias_method :_dial, :dial
10
+
11
+ def raw?
12
+ @raw
13
+ end
14
+
15
+
16
+ def listen(url, cert: nil, key: nil, ca: nil, verify: false, server_name: nil)
17
+ resolved = if cert || key || ca
18
+ cert_pem = cert ? TLS.coerce_pem(cert) : nil
19
+ key_pem = key ? TLS.coerce_pem(key) : nil
20
+ ca_pem = ca ? TLS.coerce_pem(ca) : nil
21
+ _listen_tls(url, cert_pem, key_pem, ca_pem, verify, server_name)
22
+ else
23
+ _listen(url)
24
+ end
25
+ (@urls ||= []) << (resolved || url)
26
+ self
27
+ end
28
+
29
+ def dial(url, cert: nil, key: nil, ca: nil, verify: nil, server_name: nil)
30
+ if cert || key || ca || verify == false
31
+ verify = !ca.nil? if verify.nil?
32
+ cert_pem = cert ? TLS.coerce_pem(cert) : nil
33
+ key_pem = key ? TLS.coerce_pem(key) : nil
34
+ ca_pem = ca ? TLS.coerce_pem(ca) : nil
35
+ server_name ||= TLS.extract_host(url)
36
+ _dial_tls(url, cert_pem, key_pem, ca_pem, verify, server_name)
37
+ else
38
+ _dial(url)
39
+ end
40
+ (@urls ||= []) << url
41
+ self
42
+ end
43
+
44
+ def urls
45
+ (@urls ||= []).dup.freeze
46
+ end
47
+
48
+ def name
49
+ get_opt_string('socket-name')
50
+ end
51
+
52
+ def name=(value)
53
+ set_opt_string('socket-name', value)
54
+ end
55
+
56
+ def recv_timeout
57
+ ms = get_opt_ms('recv-timeout')
58
+ ms.negative? ? nil : ms / 1000.0
59
+ end
60
+
61
+ def recv_timeout=(seconds)
62
+ set_opt_ms('recv-timeout', seconds ? (seconds * 1000).to_i : -1)
63
+ end
64
+
65
+ def send_timeout
66
+ ms = get_opt_ms('send-timeout')
67
+ ms.negative? ? nil : ms / 1000.0
68
+ end
69
+
70
+ def send_timeout=(seconds)
71
+ set_opt_ms('send-timeout', seconds ? (seconds * 1000).to_i : -1)
72
+ end
73
+
74
+ def recv_buffer
75
+ get_opt_int('recv-buffer')
76
+ end
77
+
78
+ def recv_buffer=(n)
79
+ set_opt_int('recv-buffer', n.to_i)
80
+ end
81
+
82
+ def send_buffer
83
+ get_opt_int('send-buffer')
84
+ end
85
+
86
+ def send_buffer=(n)
87
+ set_opt_int('send-buffer', n.to_i)
88
+ end
89
+
90
+ def recv_max_size
91
+ get_opt_size('recv-size-max')
92
+ end
93
+
94
+ def recv_max_size=(n)
95
+ set_opt_size('recv-size-max', n.to_i)
96
+ end
97
+
98
+ def reconnect_time
99
+ min_ms = get_opt_ms('reconnect-time-min')
100
+ max_ms = get_opt_ms('reconnect-time-max')
101
+ (min_ms / 1000.0)..(max_ms / 1000.0)
102
+ end
103
+
104
+ def reconnect_time=(range)
105
+ set_opt_ms('reconnect-time-min', (range.begin * 1000).to_i)
106
+ set_opt_ms('reconnect-time-max', (range.end * 1000).to_i)
107
+ end
108
+
109
+ def protocol_name
110
+ get_opt_string('protocol-name')
111
+ end
112
+
113
+ def peer_name
114
+ get_opt_string('peer-name')
115
+ end
116
+
117
+ def on_pipe_event
118
+ pipe_notify_start
119
+ @pipe_notify_io ||= IO.for_fd(pipe_notify_fd, autoclose: false)
120
+ return to_enum(:on_pipe_event) unless block_given?
121
+
122
+ loop do
123
+ @pipe_notify_io.wait_readable
124
+ while (event = recv_pipe_event)
125
+ yield event
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ module Socket
5
+ class Bus0
6
+ include Readable
7
+ include Writable
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ module Socket
5
+ class Pair0
6
+ include Readable
7
+ include Writable
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ module Socket
5
+ class Pair1
6
+ include Readable
7
+ include Writable
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ module Socket
5
+ class Pub0
6
+ include Writable
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ module Socket
5
+ class Pull0
6
+ include Readable
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ module Socket
5
+ class Push0
6
+ include Writable
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ module Socket
5
+ module Readable
6
+ def wait_readable(timeout = recv_timeout)
7
+ @recv_io ||= IO.for_fd(recv_fd, autoclose: false)
8
+ @recv_io.wait_readable(timeout)
9
+ end
10
+
11
+ def receive(timeout: recv_timeout)
12
+ wait_readable(timeout) or raise Timeout::Error, 'receive timed out'
13
+ super()
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ module Socket
5
+ class Rep0
6
+ include Readable
7
+ include Writable
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ module Socket
5
+ class Req0
6
+ include Readable
7
+ include Writable
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ module Socket
5
+ class Respondent0
6
+ include Readable
7
+ include Writable
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ module Socket
5
+ class Sub0
6
+ include Readable
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ module Socket
5
+ class Surveyor0
6
+ include Readable
7
+ include Writable
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ module Socket
5
+ module Writable
6
+ def wait_writable(timeout = send_timeout)
7
+ @send_io ||= IO.for_fd(send_fd, autoclose: false)
8
+ @send_io.wait_readable(timeout)
9
+ end
10
+
11
+ def send(data, timeout: send_timeout)
12
+ wait_writable(timeout) or raise Timeout::Error, 'send timed out'
13
+ super(data)
14
+ end
15
+ end
16
+ end
17
+ end
data/lib/nng/tls.rb ADDED
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+
5
+ module NNG
6
+ module TLS
7
+ module_function
8
+
9
+ def coerce_pem(val)
10
+ case val
11
+ when Pathname then val.read
12
+ else
13
+ val = val.to_pem if val.respond_to?(:to_pem)
14
+ val.to_s
15
+ end
16
+ end
17
+
18
+ def extract_host(url)
19
+ uri = URI.parse(url.sub(%r{\Atls\+tcp://}, 'tcp://'))
20
+ uri.host
21
+ rescue URI::InvalidURIError
22
+ nil
23
+ end
24
+ end
25
+ end
data/lib/nng/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module NNG
2
- VERSION = '0.1.0'
4
+ VERSION = '1.0.1'
3
5
  end
data/lib/nng.rb CHANGED
@@ -1,2 +1,25 @@
1
- require 'nng/rbnng' # Import the extension
1
+ # frozen_string_literal: true
2
+
3
+ require 'timeout'
4
+ require 'nng/rbnng'
2
5
  require 'nng/version'
6
+ require 'nng/tls'
7
+ require 'nng/socket/base'
8
+ require 'nng/socket/readable'
9
+ require 'nng/socket/writable'
10
+ require 'nng/socket/pair0'
11
+ require 'nng/socket/pair1'
12
+ require 'nng/socket/bus0'
13
+ require 'nng/socket/pub0'
14
+ require 'nng/socket/sub0'
15
+ require 'nng/socket/push0'
16
+ require 'nng/socket/pull0'
17
+ require 'nng/socket/req0'
18
+ require 'nng/socket/rep0'
19
+ require 'nng/socket/surveyor0'
20
+ require 'nng/socket/respondent0'
21
+
22
+ module NNG
23
+ Msg = Message
24
+ Error::Error = Error # backwards compatibility
25
+ end
metadata CHANGED
@@ -1,66 +1,79 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nng
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adib Saad
8
- autorequire:
8
+ - Patrik Wenger
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-27 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description:
13
+ description: Native C extension providing Ruby bindings for nng (nanomsg next generation),
14
+ a lightweight broker-less messaging library. Supports all scalability protocols,
15
+ TLS transport, and async I/O.
14
16
  email:
15
17
  - adib.saad@gmail.com
18
+ - paddor@gmail.com
16
19
  executables: []
17
20
  extensions:
18
21
  - ext/rbnng/extconf.rb
19
22
  extra_rdoc_files: []
20
23
  files:
21
- - "./ext/rbnng/bus0.c"
22
- - "./ext/rbnng/exceptions.c"
23
- - "./ext/rbnng/exceptions.h"
24
- - "./ext/rbnng/msg.c"
25
- - "./ext/rbnng/msg.h"
26
- - "./ext/rbnng/pair.c"
27
- - "./ext/rbnng/pub0.c"
28
- - "./ext/rbnng/pull0.c"
29
- - "./ext/rbnng/push0.c"
30
- - "./ext/rbnng/rbnng.c"
31
- - "./ext/rbnng/rbnng.h"
32
- - "./ext/rbnng/rep0.c"
33
- - "./ext/rbnng/req0.c"
34
- - "./ext/rbnng/respondent0.c"
35
- - "./ext/rbnng/socket.c"
36
- - "./ext/rbnng/socket.h"
37
- - "./ext/rbnng/sub0.c"
38
- - "./ext/rbnng/surveyor0.c"
39
- - "./lib/nng.rb"
40
- - "./lib/nng/version.rb"
24
+ - CHANGELOG.md
25
+ - LICENSE
26
+ - README.md
27
+ - ext/rbnng/device.c
28
+ - ext/rbnng/exceptions.c
41
29
  - ext/rbnng/extconf.rb
42
- homepage: https://github.com/adibsaad/rbnng
30
+ - ext/rbnng/message.c
31
+ - ext/rbnng/pipe.c
32
+ - ext/rbnng/rbnng.c
33
+ - ext/rbnng/rbnng.h
34
+ - ext/rbnng/socket.c
35
+ - ext/rbnng/stats.c
36
+ - ext/rbnng/tls.c
37
+ - lib/nng.rb
38
+ - lib/nng/socket/base.rb
39
+ - lib/nng/socket/bus0.rb
40
+ - lib/nng/socket/pair0.rb
41
+ - lib/nng/socket/pair1.rb
42
+ - lib/nng/socket/pub0.rb
43
+ - lib/nng/socket/pull0.rb
44
+ - lib/nng/socket/push0.rb
45
+ - lib/nng/socket/readable.rb
46
+ - lib/nng/socket/rep0.rb
47
+ - lib/nng/socket/req0.rb
48
+ - lib/nng/socket/respondent0.rb
49
+ - lib/nng/socket/sub0.rb
50
+ - lib/nng/socket/surveyor0.rb
51
+ - lib/nng/socket/writable.rb
52
+ - lib/nng/tls.rb
53
+ - lib/nng/version.rb
54
+ homepage: https://github.com/paddor/rbnng
43
55
  licenses:
44
56
  - MIT
45
- metadata: {}
46
- post_install_message:
57
+ metadata:
58
+ source_code_uri: https://github.com/paddor/rbnng
59
+ changelog_uri: https://github.com/paddor/rbnng/blob/main/CHANGELOG.md
60
+ bug_tracker_uri: https://github.com/paddor/rbnng/issues
61
+ rubygems_mfa_required: 'true'
47
62
  rdoc_options: []
48
63
  require_paths:
49
- - ext
50
64
  - lib
51
65
  required_ruby_version: !ruby/object:Gem::Requirement
52
66
  requirements:
53
67
  - - ">="
54
68
  - !ruby/object:Gem::Version
55
- version: '0'
69
+ version: '3.2'
56
70
  required_rubygems_version: !ruby/object:Gem::Requirement
57
71
  requirements:
58
72
  - - ">="
59
73
  - !ruby/object:Gem::Version
60
74
  version: '0'
61
75
  requirements: []
62
- rubygems_version: 3.1.6
63
- signing_key:
76
+ rubygems_version: 4.0.3
64
77
  specification_version: 4
65
78
  summary: Ruby bindings for nng (nanomsg-ng).
66
79
  test_files: []