cztop 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a73261e3bc3a15f8302b4e7eca58eb25393cfe301d9b755a7291bf35ef126674
4
- data.tar.gz: 5d1f75990a28eba2460cfd61e5825492bcd66604bd7990dbcdf4a74db00c0e7e
3
+ metadata.gz: 066d9600730db24e49b9b83fee30840882432df13dde7372f9e7f969b3774b33
4
+ data.tar.gz: 67ee0941443c6a9eadf7d27b4bc084e00d20c2dc7ca4448fc6459edc93a88efb
5
5
  SHA512:
6
- metadata.gz: 9ebe5b00e21a32e0b645f50a789a8818dddf31d4ed69a74c247b30b443e02cd4cdd05121f3cd2bc286c41e21e09bc6feb0b304fc245d9be6dbaf648e287c0bb8
7
- data.tar.gz: cd3c05936f8ce4c0c8c6b8f3774d70bfaf1a7b85d9770e159fb0aed4ae0628f97e74bbaa76742b7518a06d257dd6c22504dc2285a827fcc36b5bb5f59bf51ac8
6
+ metadata.gz: c8852f4b214935ef5b0302a7a6481a2f51148dfc6fac939ca9d9b5f665ab06d8242418bdbe996edb9ad6fe010a84a540fd9b24e379cdd5b3197fd3c7a80f0478
7
+ data.tar.gz: 506f6e6b9a927e8c68d60c410d3ed84abfae1edef435c43baae8855eb2f02409815692eb4cf18501298ab5c8f9c988cdd48d308a93eb20d1ae136bf51c8283fe
@@ -4,13 +4,13 @@ on: [push,pull_request]
4
4
 
5
5
  jobs:
6
6
  build:
7
- runs-on: ubuntu-20.04
7
+ runs-on: ubuntu-22.04
8
8
  steps:
9
- - uses: actions/checkout@v2
9
+ - uses: actions/checkout@v4
10
10
  - name: Set up Ruby
11
11
  uses: ruby/setup-ruby@v1
12
12
  with:
13
- ruby-version: 3.1
13
+ ruby-version: 3.3
14
14
  - name: Install CZMQ
15
15
  run: sudo apt-get install libczmq-dev
16
16
  - name: Run the default task
@@ -7,11 +7,11 @@ jobs:
7
7
  runs-on: ubuntu-22.04
8
8
  timeout-minutes: 15
9
9
  steps:
10
- - uses: actions/checkout@v2
10
+ - uses: actions/checkout@v4
11
11
  - name: Set up Ruby
12
12
  uses: ruby/setup-ruby@v1
13
13
  with:
14
- ruby-version: 3.3
14
+ ruby-version: '3.3'
15
15
  - name: Install ZMQ and CZMQ
16
16
  run: |
17
17
  export PKG_CONFIG_PATH=$HOME/lib/pkgconfig # custom libs (for linking)
@@ -8,12 +8,11 @@ jobs:
8
8
  strategy:
9
9
  matrix:
10
10
  ruby:
11
- - 3.0
12
- - 3.1
13
- - 3.2
14
- - 3.3
11
+ - '3.1'
12
+ - '3.2'
13
+ - '3.3'
15
14
  steps:
16
- - uses: actions/checkout@v2
15
+ - uses: actions/checkout@v4
17
16
  - name: Set up Ruby ${{ matrix.ruby }}
18
17
  uses: ruby/setup-ruby@v1
19
18
  with:
data/.projections.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "lib/*.rb": { "alternate": "spec/{}_spec.rb" },
3
+ "spec/*_spec.rb": { "alternate": "lib/{}.rb", "dispatch": "bundle exec rspec -ff {file}" }
4
+ }
data/CHANGES.md CHANGED
@@ -1,3 +1,12 @@
1
+ 1.1.1 (1/4/2024)
2
+ -----
3
+ * speed up Async::IO#wait_readable and #wait_writable
4
+ * update CI and specs
5
+
6
+ 1.1.0 (1/4/2024)
7
+ -----
8
+ * support Async
9
+
1
10
  1.1.0.pre1 (10/17/2022)
2
11
  -----
3
12
  * modernize syntax using Rubocop
data/README.md CHANGED
@@ -10,6 +10,76 @@ FFI binding of [CZMQ](https://github.com/zeromq/czmq) and has a focus on being
10
10
  easy to use for Rubyists (POLS) and providing first class support for security
11
11
  mechanisms (like CURVE).
12
12
 
13
+
14
+ ## Example with Async
15
+
16
+ ```ruby
17
+ #! /usr/bin/env ruby
18
+
19
+ require 'cztop/async'
20
+
21
+ Async do |task|
22
+ task.async do |t|
23
+ socket = CZTop::Socket::REP.new("inproc://req_rep_example")
24
+ io = Async::IO.try_convert socket
25
+
26
+ socket.options.rcvtimeo = 50 # ms
27
+
28
+ loop do
29
+ msg = io.receive
30
+ puts "<<< #{msg.to_a.inspect}"
31
+ io << msg.to_a.map(&:upcase)
32
+ rescue IO::TimeoutError
33
+ break
34
+ end
35
+
36
+ puts "REP done."
37
+ end
38
+
39
+ task.async do
40
+ socket = CZTop::Socket::REQ.new("inproc://req_rep_example")
41
+ io = Async::IO.try_convert socket
42
+
43
+ 10.times do |i|
44
+ io << "foobar ##{i}"
45
+ msg = io.receive
46
+ puts ">>> #{msg.to_a.inspect}"
47
+ end
48
+
49
+ puts "REQ done."
50
+ end
51
+ end
52
+ ```
53
+
54
+
55
+ Output:
56
+ ```
57
+ <<< ["foobar #0"]
58
+ >>> ["FOOBAR #0"]
59
+ <<< ["foobar #1"]
60
+ >>> ["FOOBAR #1"]
61
+ <<< ["foobar #2"]
62
+ >>> ["FOOBAR #2"]
63
+ <<< ["foobar #3"]
64
+ >>> ["FOOBAR #3"]
65
+ <<< ["foobar #4"]
66
+ >>> ["FOOBAR #4"]
67
+ <<< ["foobar #5"]
68
+ >>> ["FOOBAR #5"]
69
+ <<< ["foobar #6"]
70
+ >>> ["FOOBAR #6"]
71
+ <<< ["foobar #7"]
72
+ >>> ["FOOBAR #7"]
73
+ <<< ["foobar #8"]
74
+ >>> ["FOOBAR #8"]
75
+ <<< ["foobar #9"]
76
+ >>> ["FOOBAR #9"]
77
+ REQ done.
78
+ REP done.
79
+ 0.46user 0.09system 0:00.60elapsed 90%CPU (0avgtext+0avgdata 47296maxresident)k
80
+ 0inputs+0outputs (0major+13669minor)pagefaults 0swaps
81
+ ```
82
+
13
83
  ## Overview
14
84
 
15
85
  ### Class Hierarchy
@@ -59,24 +129,12 @@ More information in the [API documentation](http://www.rubydoc.info/github/paddo
59
129
 
60
130
  ### Features
61
131
 
132
+ * Ruby idiomatic API
62
133
  * compatible with [Async](https://github.com/socketry/async) / [Async::IO](https://github.com/socketry/async-io)
63
- * Ruby-like API
64
- * method names
65
- * sending a message via a socket is done with `Socket#<<`
66
- * `socket << "simple message"`
67
- * `socket << ["multi", "frame", "message"]`
68
- * `#x=` methods instead of `#set_x` (e.g. socket options)
69
- * `#[]` where it makes sense (e.g. on a Message, Config, or Certificate)
70
- * no manual error checking needed
71
- * if there's an error, an appropriate exception is raised
72
- * of course, no manual dealing with the ZMQ context
73
- * easy security
74
- * use `Socket#CURVE_server!(cert)` on the server
75
- * and `Socket#CURVE_client!(client_cert, server_cert)` on the client
76
- * socket types as Ruby classes
77
- * no need to manually pass type constants
78
- * but you can: `CZTop::Socket.new_by_type(:REP)`
79
- * e.g. `#subscribe` only exists on `CZTop::Socket::SUB`
134
+ * errors as exceptions
135
+ * CURVE security
136
+ * supports CZMQ DRAFT API
137
+ * extensive spec coverage
80
138
 
81
139
  ## Requirements
82
140
 
@@ -95,9 +153,11 @@ On macOS using Homebrew, run:
95
153
 
96
154
  ### Supported Rubies
97
155
 
98
- * MRI (3.0, 3.2, 3.3)
99
- * Rubinius (HEAD)
100
- * JRuby 9000 (HEAD)
156
+ At least:
157
+
158
+ * Ruby 3.0, 3.1, 3.2, 3.3
159
+
160
+ Using CZTop with Async will require Ruby 3.2 because of the use of `IO::TimeoutError`, which was introduced in Ruby 3.2.
101
161
 
102
162
  ## Installation
103
163
 
data/cztop.gemspec CHANGED
@@ -8,7 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ["Patrik Wenger"]
9
9
  spec.email = ["paddor@gmail.com"]
10
10
 
11
- spec.summary = %q{CZMQ Ruby binding based on the generated low-level FFI bindings of CZMQ}
11
+ spec.summary = 'CZMQ FFI binding to bring ZMQ sockets to Ruby'
12
+ spec.description = 'CZMQ binding based on the generated low-level FFI bindings of CZMQ'
12
13
  spec.homepage = "https://rubygems.org/gems/cztop"
13
14
  spec.license = "ISC"
14
15
  spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")
@@ -22,7 +23,7 @@ Gem::Specification.new do |spec|
22
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
24
  spec.require_paths = ["lib"]
24
25
 
25
- spec.add_runtime_dependency "czmq-ffi-gen", "~> 1.1.0.pre1"
26
+ spec.add_runtime_dependency "czmq-ffi-gen", "~> 1.1.0"
26
27
 
27
28
  spec.add_development_dependency "bundler"
28
29
  spec.add_development_dependency "rake"
@@ -32,6 +33,9 @@ Gem::Specification.new do |spec|
32
33
  spec.add_development_dependency "pry"
33
34
  spec.add_development_dependency "yard"
34
35
  spec.add_development_dependency "rubocop", "~> 1.36.0"
35
- spec.add_development_dependency "async", ">= 2.0.1"
36
- spec.add_development_dependency "async-io"
36
+
37
+ if RUBY_VERSION >= '3.1'
38
+ spec.add_development_dependency "async", ">= 2.0.1"
39
+ spec.add_development_dependency "async-io"
40
+ end
37
41
  end
@@ -4,37 +4,32 @@ require 'cztop/async'
4
4
 
5
5
  Async do |task|
6
6
  task.async do |t|
7
- socket = CZTop::Socket::REP.new("ipc:///tmp/req_rep_example")
7
+ socket = CZTop::Socket::REP.new("inproc://req_rep_example")
8
+ io = Async::IO.try_convert socket
8
9
 
9
- # Simply echo every message, with every frame String#upcase'd.
10
- socket.options.rcvtimeo = 3
11
- io = Async::IO.try_convert socket
10
+ socket.options.rcvtimeo = 50 # ms
12
11
 
13
- msg = io.receive
14
- puts "<<< #{msg.to_a.inspect}"
15
- io << msg.to_a.map(&:upcase)
12
+ loop do
13
+ msg = io.receive
14
+ puts "<<< #{msg.to_a.inspect}"
15
+ io << msg.to_a.map(&:upcase)
16
+ rescue IO::TimeoutError
17
+ break
18
+ end
16
19
 
17
20
  puts "REP done."
18
21
  end
19
22
 
20
23
  task.async do
21
- socket = CZTop::Socket::REQ.new("ipc:///tmp/req_rep_example")
22
- puts ">>> Socket connected."
24
+ socket = CZTop::Socket::REQ.new("inproc://req_rep_example")
25
+ io = Async::IO.try_convert socket
23
26
 
24
- io = Async::IO.try_convert socket
25
- # sleep 5
26
- io << "foobar"
27
+ 10.times do |i|
28
+ io << "foobar ##{i}"
29
+ msg = io.receive
30
+ puts ">>> #{msg.to_a.inspect}"
31
+ end
27
32
 
28
- socket.options.rcvtimeo = 3
29
- msg = io.receive
30
- puts ">>> #{msg.to_a.inspect}"
31
33
  puts "REQ done."
32
34
  end
33
-
34
- task.async do
35
- 6.times do
36
- sleep 0.5
37
- puts "tick"
38
- end
39
- end
40
35
  end
data/lib/cztop/async.rb CHANGED
@@ -49,6 +49,8 @@ module Async
49
49
  def wait_readable(timeout = read_timeout)
50
50
  @io_fd ||= ::IO.for_fd @io.fd, autoclose: false
51
51
 
52
+ return true if @io.readable?
53
+
52
54
  if timeout
53
55
  timeout_at = now + timeout
54
56
 
@@ -67,6 +69,8 @@ module Async
67
69
  def wait_writable(timeout = write_timeout)
68
70
  @io_fd ||= ::IO.for_fd @io.fd, autoclose: false
69
71
 
72
+ return true if @io.writable?
73
+
70
74
  if timeout
71
75
  timeout_at = now + timeout
72
76
 
@@ -13,6 +13,12 @@ module CZTop
13
13
  include HasFFIDelegate
14
14
  extend CZTop::HasFFIDelegate::ClassMethods
15
15
 
16
+ unless ::CZMQ::FFI::Zsys.has_curve
17
+ def self.new(...)
18
+ fail NotImplementedError
19
+ end
20
+ end
21
+
16
22
  # Initializes a new certificate store.
17
23
  #
18
24
  # @param location [String, #to_s, nil] location the path to the
@@ -8,6 +8,13 @@ module CZTop
8
8
  extend CZTop::HasFFIDelegate::ClassMethods
9
9
  include ::CZMQ::FFI
10
10
 
11
+ unless ::CZMQ::FFI::Zsys.has_curve
12
+ def self.new(...)
13
+ fail NotImplementedError
14
+ end
15
+ end
16
+
17
+
11
18
  # Warns if CURVE security isn't available.
12
19
  # @return [void]
13
20
  def self.check_curve_availability
@@ -57,6 +64,16 @@ module CZTop
57
64
  end
58
65
 
59
66
 
67
+ KEY_ALL_ZERO = '0000000000000000000000000000000000000000'
68
+
69
+ # @return [Boolean] whether one of the keys is all zeros (happens when CURVE is not available, i.e. libzmq was
70
+ # compiled without libsodium)
71
+ # @see .check_curve_availability
72
+ def zero?
73
+ public_key(format: :z85) == KEY_ALL_ZERO || secret_key(format: :z85) == KEY_ALL_ZERO
74
+ end
75
+
76
+
60
77
  # Returns the public key either as Z85-encoded ASCII string (default) or
61
78
  # binary string.
62
79
  # @param format [Symbol] +:z85+ for Z85, +:binary+ for binary
data/lib/cztop/version.rb CHANGED
@@ -2,6 +2,6 @@
2
2
 
3
3
  module CZTop
4
4
 
5
- VERSION = '1.1.0'
5
+ VERSION = '1.1.1'
6
6
 
7
7
  end
@@ -111,58 +111,8 @@ module CZTop
111
111
 
112
112
  # @!endgroup
113
113
 
114
- # @!group Security Mechanisms
115
-
116
- # @return [Boolean] whether this zocket is a CURVE server
117
- def CURVE_server?
118
- Zsock.curve_server(@zocket).positive?
119
- end
120
-
121
-
122
- # Make this zocket a CURVE server.
123
- # @param bool [Boolean]
124
- # @note You'll have to use a {CZTop::Authenticator}.
125
- def CURVE_server=(bool)
126
- Zsock.set_curve_server(@zocket, bool ? 1 : 0)
127
- end
128
-
129
-
130
- # @return [String] Z85 encoded server key set
131
- # @return [nil] if the current mechanism isn't CURVE or CURVE isn't
132
- # supported
133
- def CURVE_serverkey
134
- CURVE_key(:curve_serverkey)
135
- end
136
114
 
137
-
138
- # Get one of the CURVE keys.
139
- # @param key_name [Symbol] something like +:curve_serverkey+
140
- # @return [String, nil] key, if CURVE is supported and active, or nil
141
- def CURVE_key(key_name)
142
- return nil if mechanism != :CURVE
143
-
144
- ptr = Zsock.__send__(key_name, @zocket)
145
- return nil if ptr.null?
146
-
147
- ptr.read_string
148
- end
149
- private :CURVE_key
150
-
151
- # Sets the server's public key, so the zocket can authenticate the
152
- # remote server.
153
- # @param key [String] Z85 (40 bytes) or binary (32 bytes) server key
154
- # @raise [ArgumentError] if key has wrong size
155
- def CURVE_serverkey=(key)
156
- case key.bytesize
157
- when 40
158
- Zsock.set_curve_serverkey(@zocket, key)
159
- when 32
160
- ptr = ::FFI::MemoryPointer.from_string(key)
161
- Zsock.set_curve_serverkey_bin(@zocket, ptr)
162
- else
163
- raise ArgumentError, format('invalid server key: %p', key)
164
- end
165
- end
115
+ # @!group Security Mechanisms
166
116
 
167
117
  # supported security mechanisms and their macro value equivalent
168
118
  MECHANISMS = {
@@ -182,20 +132,92 @@ module CZTop
182
132
  raise format('unknown ZMQ security mechanism code: %i', code)
183
133
  end
184
134
 
135
+ if ::CZMQ::FFI::Zsys.has_curve
136
+ # @return [Boolean] whether this zocket is a CURVE server
137
+ def CURVE_server?
138
+ Zsock.curve_server(@zocket).positive?
139
+ end
185
140
 
186
- # @return [String] Z85 encoded secret key set
187
- # @return [nil] if the current mechanism isn't CURVE or CURVE isn't
188
- # supported
189
- def CURVE_secretkey
190
- CURVE_key(:curve_secretkey)
191
- end
141
+
142
+ # Make this zocket a CURVE server.
143
+ # @param bool [Boolean]
144
+ # @note You'll have to use a {CZTop::Authenticator}.
145
+ def CURVE_server=(bool)
146
+ Zsock.set_curve_server(@zocket, bool ? 1 : 0)
147
+ end
148
+
149
+
150
+ # @return [String] Z85 encoded server key set
151
+ # @return [nil] if the current mechanism isn't CURVE or CURVE isn't
152
+ # supported
153
+ def CURVE_serverkey
154
+ CURVE_key(:curve_serverkey)
155
+ end
156
+
157
+
158
+ # Get one of the CURVE keys.
159
+ # @param key_name [Symbol] something like +:curve_serverkey+
160
+ # @return [String, nil] key, if CURVE is supported and active, or nil
161
+ def CURVE_key(key_name)
162
+ return nil if mechanism != :CURVE
163
+
164
+ ptr = Zsock.__send__(key_name, @zocket)
165
+ return nil if ptr.null?
166
+
167
+ ptr.read_string
168
+ end
169
+ private :CURVE_key
170
+
171
+ # Sets the server's public key, so the zocket can authenticate the
172
+ # remote server.
173
+ # @param key [String] Z85 (40 bytes) or binary (32 bytes) server key
174
+ # @raise [ArgumentError] if key has wrong size
175
+ def CURVE_serverkey=(key)
176
+ case key.bytesize
177
+ when 40
178
+ Zsock.set_curve_serverkey(@zocket, key)
179
+ when 32
180
+ ptr = ::FFI::MemoryPointer.from_string(key)
181
+ Zsock.set_curve_serverkey_bin(@zocket, ptr)
182
+ else
183
+ raise ArgumentError, format('invalid server key: %p', key)
184
+ end
185
+ end
192
186
 
193
187
 
194
- # @return [String] Z85 encoded public key set
195
- # @return [nil] if the current mechanism isn't CURVE or CURVE isn't
196
- # supported
197
- def CURVE_publickey
198
- CURVE_key(:curve_publickey)
188
+ # @return [String] Z85 encoded secret key set
189
+ # @return [nil] if the current mechanism isn't CURVE or CURVE isn't
190
+ # supported
191
+ def CURVE_secretkey
192
+ CURVE_key(:curve_secretkey)
193
+ end
194
+
195
+
196
+ # @return [String] Z85 encoded public key set
197
+ # @return [nil] if the current mechanism isn't CURVE or CURVE isn't
198
+ # supported
199
+ def CURVE_publickey
200
+ CURVE_key(:curve_publickey)
201
+ end
202
+ else
203
+ def CURVE_server?
204
+ fail NotImplementedError
205
+ end
206
+ def CURVE_server=(...)
207
+ fail NotImplementedError
208
+ end
209
+ def CURVE_serverkey(...)
210
+ fail NotImplementedError
211
+ end
212
+ def CURVE_serverkey=(...)
213
+ fail NotImplementedError
214
+ end
215
+ def CURVE_secretkey(...)
216
+ fail NotImplementedError
217
+ end
218
+ def CURVE_publickey(...)
219
+ fail NotImplementedError
220
+ end
199
221
  end
200
222
 
201
223
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cztop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrik Wenger
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.1.0.pre1
19
+ version: 1.1.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.1.0.pre1
26
+ version: 1.1.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -164,7 +164,7 @@ dependencies:
164
164
  - - ">="
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
- description:
167
+ description: CZMQ binding based on the generated low-level FFI bindings of CZMQ
168
168
  email:
169
169
  - paddor@gmail.com
170
170
  executables:
@@ -177,6 +177,7 @@ files:
177
177
  - ".github/workflows/draft_api.yml"
178
178
  - ".github/workflows/stable_api.yml"
179
179
  - ".gitignore"
180
+ - ".projections.json"
180
181
  - ".rspec"
181
182
  - ".rubocop.yml"
182
183
  - ".yardopts"
@@ -273,5 +274,5 @@ requirements: []
273
274
  rubygems_version: 3.5.3
274
275
  signing_key:
275
276
  specification_version: 4
276
- summary: CZMQ Ruby binding based on the generated low-level FFI bindings of CZMQ
277
+ summary: CZMQ FFI binding to bring ZMQ sockets to Ruby
277
278
  test_files: []