cztop 1.0.0 → 1.1.0.pre1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/coverage.yml +20 -0
  3. data/.github/workflows/draft_api.yml +27 -0
  4. data/.github/workflows/{main.yml → stable_api.yml} +6 -6
  5. data/.rubocop.yml +175 -0
  6. data/CHANGES.md +8 -1
  7. data/Gemfile +5 -0
  8. data/README.md +3 -1
  9. data/ci/install-libczmq +22 -0
  10. data/ci/install-libzmq +22 -0
  11. data/cztop.gemspec +3 -2
  12. data/lib/cztop/actor.rb +55 -26
  13. data/lib/cztop/authenticator.rb +18 -9
  14. data/lib/cztop/beacon.rb +22 -10
  15. data/lib/cztop/cert_store.rb +8 -2
  16. data/lib/cztop/certificate.rb +47 -18
  17. data/lib/cztop/config/comments.rb +14 -3
  18. data/lib/cztop/config/serialization.rb +25 -5
  19. data/lib/cztop/config/traversing.rb +44 -13
  20. data/lib/cztop/config.rb +23 -9
  21. data/lib/cztop/frame.rb +23 -10
  22. data/lib/cztop/has_ffi_delegate.rb +11 -1
  23. data/lib/cztop/message/frames.rb +16 -2
  24. data/lib/cztop/message.rb +36 -22
  25. data/lib/cztop/metadata.rb +35 -24
  26. data/lib/cztop/monitor.rb +14 -5
  27. data/lib/cztop/poller/aggregated.rb +31 -15
  28. data/lib/cztop/poller/zmq.rb +25 -22
  29. data/lib/cztop/poller/zpoller.rb +18 -6
  30. data/lib/cztop/poller.rb +43 -18
  31. data/lib/cztop/polymorphic_zsock_methods.rb +6 -1
  32. data/lib/cztop/proxy.rb +34 -19
  33. data/lib/cztop/send_receive_methods.rb +5 -1
  34. data/lib/cztop/socket/types.rb +128 -22
  35. data/lib/cztop/socket.rb +23 -18
  36. data/lib/cztop/version.rb +5 -1
  37. data/lib/cztop/z85/padded.rb +12 -3
  38. data/lib/cztop/z85/pipe.rb +40 -17
  39. data/lib/cztop/z85.rb +17 -6
  40. data/lib/cztop/zap.rb +57 -32
  41. data/lib/cztop/zsock_options.rb +155 -122
  42. data/lib/cztop.rb +2 -1
  43. metadata +28 -10
  44. data/.ruby-version +0 -1
@@ -1,18 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Can be used if you want to encode or decode data from one IO to another.
2
4
  # It'll do so until it hits EOF in the source IO.
3
5
  class CZTop::Z85::Pipe
6
+
4
7
  # @param source [IO] where to read data
5
8
  # @param sink [IO] where to write data
6
9
  # @param strategy [Strategy] algorithm to use (pass the class itself,
7
10
  # not an instance)
8
11
  def initialize(source, sink, strategy: Strategy::Parallel)
9
- @source, @sink = source, sink
10
- @strategy = strategy
12
+ @source = source
13
+ @sink = sink
14
+ @strategy = strategy
11
15
  @bin_bytes = 0 # processed binary data (non-Z85)
12
16
  end
13
17
 
14
18
  # @return [Integer] size of chunks read when encoding
15
- ENCODE_READ_SZ = (32 * 2**10) # 32 KiB (for full chunks read)
19
+ ENCODE_READ_SZ = (32 * (2**10)) # 32 KiB (for full chunks read)
16
20
 
17
21
  # @return [Integer] size of chunks read when decoding
18
22
  DECODE_READ_SZ = ENCODE_READ_SZ / 4 * 5
@@ -29,14 +33,15 @@ class CZTop::Z85::Pipe
29
33
  elsif prev_chunk && chunk.nil? # last chunk
30
34
  CZTop::Z85::Padded.encode(prev_chunk)
31
35
  elsif prev_chunk.nil? && chunk.nil?
32
- CZTop::Z85.encode("") # empty input
36
+ CZTop::Z85.encode('') # empty input
33
37
  else
34
- "" # very first chunk. don't encode anything yet...
38
+ '' # very first chunk. don't encode anything yet...
35
39
  end
36
40
  end.execute
37
- return @bin_bytes
41
+ @bin_bytes
38
42
  end
39
43
 
44
+
40
45
  # Decodes Z85 data from source and writes decoded data to sink. This is
41
46
  # done until EOF is hit on the source.
42
47
  #
@@ -48,19 +53,21 @@ class CZTop::Z85::Pipe
48
53
  elsif prev_chunk && chunk.nil?
49
54
  CZTop::Z85::Padded.decode(prev_chunk)
50
55
  elsif prev_chunk.nil? && chunk.nil?
51
- CZTop::Z85.decode("") # empty input
56
+ CZTop::Z85.decode('') # empty input
52
57
  else
53
- "" # very first chunk. don't decode anything yet...
58
+ '' # very first chunk. don't decode anything yet...
54
59
  end
55
60
  end.execute
56
- return @bin_bytes
61
+ @bin_bytes
57
62
  end
58
63
 
64
+
59
65
  # @abstract
60
66
  # Different encoding/decoding strategies (algorithms).
61
67
  #
62
68
  # This is mainly just for me to practice the GoF Strategy Pattern.
63
69
  class Strategy
70
+
64
71
  # @param source [IO] the source
65
72
  # @param sink [IO] the sink
66
73
  # @param read_sz [Integer] chunk size when reading from source
@@ -71,33 +78,41 @@ class CZTop::Z85::Pipe
71
78
  # the first time)
72
79
  # @yieldreturn [String] encoded/decoded chunk to write to sink
73
80
  def initialize(source, sink, read_sz, &xcode)
74
- @source = source
75
- @sink = sink
81
+ @source = source
82
+ @sink = sink
76
83
  @read_sz = read_sz
77
- @xcode = xcode
84
+ @xcode = xcode
78
85
  end
79
86
 
87
+
80
88
  # @abstract
81
89
  # Runs the algorithm.
82
90
  # @raise [void]
83
- def execute() raise NotImplementedError end
91
+ def execute
92
+ raise NotImplementedError
93
+ end
94
+
84
95
 
85
96
  # A single thread that is either reading input, encoding/decoding, or
86
97
  # writing output.
87
98
  class Sequential < Strategy
99
+
88
100
  # Runs the algorithm.
89
101
  # @raise [void]
90
102
  def execute
91
103
  previous_chunk = nil
92
104
  while true
93
105
  chunk = @source.read(@read_sz)
94
- @sink << @xcode.(chunk, previous_chunk)
106
+ @sink << @xcode.call(chunk, previous_chunk)
95
107
  break if chunk.nil?
108
+
96
109
  previous_chunk = chunk
97
110
  end
98
111
  end
112
+
99
113
  end
100
114
 
115
+
101
116
  # Uses three threads:
102
117
  #
103
118
  # 1. reads from source
@@ -108,6 +123,7 @@ class CZTop::Z85::Pipe
108
123
  # platforms such as Rubinius and JRuby (and multiple CPU cores).
109
124
  #
110
125
  class Parallel < Strategy
126
+
111
127
  # Initializes the 2 sized queues used.
112
128
  def initialize(*)
113
129
  super
@@ -126,6 +142,7 @@ class CZTop::Z85::Pipe
126
142
  # @sink
127
143
  end
128
144
 
145
+
129
146
  # Runs the algorithm.
130
147
  # @raise [void]
131
148
  def execute
@@ -140,12 +157,13 @@ class CZTop::Z85::Pipe
140
157
  # pushes a +nil+ into the queue.
141
158
  # @return [void]
142
159
  def read
143
- while chunk = @source.read(@read_sz)
160
+ while (chunk = @source.read(@read_sz))
144
161
  @source_queue << chunk
145
162
  end
146
163
  @source_queue << nil
147
164
  end
148
165
 
166
+
149
167
  # Pops all chunks from the source queue, encodes or decodes them,
150
168
  # and pushes the result into the sink queue. Then pushes a +nil+
151
169
  # into the queue.
@@ -157,21 +175,26 @@ class CZTop::Z85::Pipe
157
175
  chunk = @source_queue.pop
158
176
 
159
177
  # call @xcode for the trailing nil-chunk as well
160
- @sink_queue << @xcode.(chunk, previous_chunk)
178
+ @sink_queue << @xcode.call(chunk, previous_chunk)
161
179
 
162
180
  break if chunk.nil?
181
+
163
182
  previous_chunk = chunk
164
183
  end
165
184
  @sink_queue << nil
166
185
  end
167
186
 
187
+
168
188
  # Pops all chunks from the sink queue and writes them to the sink.
169
189
  # @return [void]
170
190
  def write
171
- while chunk = @sink_queue.pop
191
+ while (chunk = @sink_queue.pop)
172
192
  @sink << chunk
173
193
  end
174
194
  end
195
+
175
196
  end
197
+
176
198
  end
199
+
177
200
  end
data/lib/cztop/z85.rb CHANGED
@@ -1,13 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CZTop
2
4
  # Represents a CZMQ::FFI::Zarmour in Z85 mode.
3
5
  #
4
6
  # Use this class to encode to and from the Z85 encoding scheme.
5
7
  # @see http://rfc.zeromq.org/spec:32
6
8
  class Z85
9
+
7
10
  include HasFFIDelegate
8
11
  extend CZTop::HasFFIDelegate::ClassMethods
9
12
 
10
13
  class << self
14
+
11
15
  # Same as {Z85#encode}, but without the need to create an instance
12
16
  # first.
13
17
  #
@@ -20,6 +24,7 @@ module CZTop
20
24
  default.encode(input)
21
25
  end
22
26
 
27
+
23
28
  # Same as {Z85#decode}, but without the need to create an instance
24
29
  # first.
25
30
  #
@@ -39,6 +44,7 @@ module CZTop
39
44
  def default
40
45
  @default ||= Z85.new
41
46
  end
47
+
42
48
  end
43
49
 
44
50
  def initialize
@@ -46,6 +52,7 @@ module CZTop
46
52
  ffi_delegate.set_mode(CZMQ::FFI::Zarmour::MODE_Z85)
47
53
  end
48
54
 
55
+
49
56
  # Encodes to Z85.
50
57
  # @param input [String] possibly binary input data
51
58
  # @return [String] Z85 encoded data as ASCII string
@@ -53,15 +60,17 @@ module CZTop
53
60
  # remainder
54
61
  # @raise [SystemCallError] if this fails
55
62
  def encode(input)
56
- raise ArgumentError, "wrong input length" if input.bytesize % 4 > 0
63
+ raise ArgumentError, 'wrong input length' if (input.bytesize % 4).positive?
64
+
57
65
  input = input.dup.force_encoding(Encoding::BINARY)
58
- ptr = ffi_delegate.encode(input, input.bytesize)
66
+ ptr = ffi_delegate.encode(input, input.bytesize)
59
67
  raise_zmq_err if ptr.null?
60
68
  z85 = ptr.read_string
61
69
  z85.encode!(Encoding::ASCII)
62
- return z85
70
+ z85
63
71
  end
64
72
 
73
+
65
74
  # Decodes from Z85.
66
75
  # @param input [String] Z85 encoded data
67
76
  # @return [String] original data as binary string
@@ -69,11 +78,13 @@ module CZTop
69
78
  # remainder
70
79
  # @raise [SystemCallError] if this fails
71
80
  def decode(input)
72
- raise ArgumentError, "wrong input length" if input.bytesize % 5 > 0
81
+ return '' if input.empty?
82
+ raise ArgumentError, 'wrong input length' if (input.bytesize % 5).positive?
83
+
73
84
  zchunk = ffi_delegate.decode(input)
74
85
  raise_zmq_err if zchunk.null?
75
- decoded_string = zchunk.data.read_string(zchunk.size - 1)
76
- return decoded_string
86
+ zchunk.data.read_string(zchunk.size - 1)
77
87
  end
88
+
78
89
  end
79
90
  end
data/lib/cztop/zap.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CZTop
2
4
  # This module provides two classes useful when implementing your own ZAP
3
5
  # authentication handler or when directly communicating with one. Within
@@ -12,51 +14,58 @@ module CZTop
12
14
  # @note This is not needed to be able to use {CZTop::Authenticator}!
13
15
  # @see https://rfc.zeromq.org/spec:27/ZAP
14
16
  module ZAP
17
+
15
18
  # the endpoint a ZAP authenticator has bound to
16
- ENDPOINT = "inproc://zeromq.zap.01"
19
+ ENDPOINT = 'inproc://zeromq.zap.01'
17
20
 
18
21
  # the ZAP version supported by this code
19
- VERSION = "1.0"
22
+ VERSION = '1.0'
20
23
 
21
24
  # superclass for ZAP errors
22
25
  class Error < StandardError
23
26
  end
24
27
 
28
+
25
29
  # used when the response contains an unsupported version
26
30
  class VersionMismatch < Error
27
31
  end
28
32
 
33
+
29
34
  # security mechanisms mentioned in ZeroMQ RFC 27.
30
35
  module Mechanisms
31
- NULL = "NULL"
32
- PLAIN = "PLAIN"
33
- CURVE = "CURVE"
36
+
37
+ NULL = 'NULL'
38
+ PLAIN = 'PLAIN'
39
+ CURVE = 'CURVE'
40
+
34
41
  end
35
42
 
43
+
36
44
  # Represents a ZAP request.
37
45
  class Request
46
+
38
47
  # Crafts a new {Request} from a message.
39
48
  #
40
49
  # @param msg [CZTop::message] the message
41
50
  # @return [Request] the request
42
51
  # @raise [VersionMismatch] if the message contains an unsupported version
43
52
  def self.from_message(msg)
44
- version, # The version frame, which SHALL contain the three octets "1.0".
45
- request_id, # The request id, which MAY contain an opaque binary blob.
46
- domain, # The domain, which SHALL contain a string.
47
- address, # The address, the origin network IP address.
48
- identity, # The identity, the connection Identity, if any.
49
- mechanism, # The mechanism, which SHALL contain a string.
50
- *credentials = # The credentials, which SHALL be zero or more opaque frames.
51
- msg.to_a
53
+ version, # The version frame, which SHALL contain the three octets "1.0".
54
+ request_id, # The request id, which MAY contain an opaque binary blob.
55
+ domain, # The domain, which SHALL contain a string.
56
+ address, # The address, the origin network IP address.
57
+ identity, # The identity, the connection Identity, if any.
58
+ mechanism, # The mechanism, which SHALL contain a string.
59
+ *credentials = # The credentials, which SHALL be zero or more opaque frames.
60
+ msg.to_a
52
61
 
53
62
  raise VersionMismatch if version != VERSION
54
63
 
55
64
  new(domain, credentials, mechanism: mechanism).tap do |r|
56
- r.version = version
65
+ r.version = version
57
66
  r.request_id = request_id
58
- r.address = address
59
- r.identity = identity
67
+ r.address = address
68
+ r.identity = identity
60
69
  end
61
70
  end
62
71
 
@@ -89,22 +98,25 @@ module CZTop
89
98
  # @param credentials [Array<String>] the credentials of the user,
90
99
  # depending on the security mechanism used
91
100
  def initialize(domain, credentials = [], mechanism: Mechanisms::CURVE)
92
- @domain = domain
101
+ @domain = domain
93
102
  @credentials = credentials
94
- @mechanism = mechanism
95
- @version = VERSION
103
+ @mechanism = mechanism
104
+ @version = VERSION
96
105
  end
97
106
 
107
+
98
108
  # Creates a sendable message from this {Request}.
99
109
  # @return [CZTop::Message} this request packed into a message
100
110
  def to_msg
101
- fields = [ @version, @request_id, @domain, @address,
102
- @identity, @mechanism, @credentials].flatten.map(&:to_s)
111
+ fields = [@version, @request_id, @domain, @address,
112
+ @identity, @mechanism, @credentials].flatten.map(&:to_s)
103
113
 
104
114
  CZTop::Message.new(fields)
105
115
  end
116
+
106
117
  end
107
118
 
119
+
108
120
  # Represents a ZAP response.
109
121
  class Response
110
122
 
@@ -112,23 +124,27 @@ module CZTop
112
124
  class TemporaryError < Error
113
125
  end
114
126
 
127
+
115
128
  # used to indicate an internal error of the authenticator
116
129
  class InternalError < Error
117
130
  end
118
131
 
132
+
119
133
  # Status codes of ZAP responses.
120
134
  module StatusCodes
121
- SUCCESS = "200"
122
- TEMPORARY_ERROR = "300"
123
- AUTHENTICATION_FAILURE = "400"
124
- INTERNAL_ERROR = "500"
135
+
136
+ SUCCESS = '200'
137
+ TEMPORARY_ERROR = '300'
138
+ AUTHENTICATION_FAILURE = '400'
139
+ INTERNAL_ERROR = '500'
125
140
 
126
141
  ALL = [
127
142
  SUCCESS,
128
143
  TEMPORARY_ERROR,
129
144
  AUTHENTICATION_FAILURE,
130
145
  INTERNAL_ERROR
131
- ]
146
+ ].freeze
147
+
132
148
  end
133
149
 
134
150
  include StatusCodes
@@ -160,16 +176,16 @@ module CZTop
160
176
  when INTERNAL_ERROR
161
177
  raise InternalError, status_text
162
178
  else
163
- raise InternalError, "invalid status code"
179
+ raise InternalError, 'invalid status code'
164
180
  end
165
181
 
166
182
  new(status_code).tap do |r|
167
- r.version = version
168
- r.request_id = request_id
183
+ r.version = version
184
+ r.request_id = request_id
169
185
  r.status_code = status_code
170
186
  r.status_text = status_text
171
- r.user_id = user_id
172
- r.meta_data = meta_data
187
+ r.user_id = user_id
188
+ r.meta_data = meta_data
173
189
  end
174
190
  end
175
191
 
@@ -198,30 +214,37 @@ module CZTop
198
214
  def initialize(status_code)
199
215
  @status_code = status_code.to_s
200
216
  raise ArgumentError unless ALL.include?(@status_code)
201
- @version = VERSION
217
+
218
+ @version = VERSION
202
219
  end
203
220
 
221
+
204
222
  # @return [Boolean] whether the authentication was successful
205
223
  def success?
206
224
  @status_code == SUCCESS
207
225
  end
208
226
 
227
+
209
228
  # Returns the user ID, if authentication was successful.
210
229
  # @return [String] the user ID of the authenticated user
211
230
  # @return [nil] if authentication was unsuccessful
212
231
  def user_id
213
232
  return nil unless success?
233
+
214
234
  @user_id
215
235
  end
216
236
 
237
+
217
238
  # Returns the meta data, if authentication was successful.
218
239
  # @return [String] the meta data for the authenticated user
219
240
  # @return [nil] if authentication was unsuccessful
220
241
  def meta_data
221
242
  return nil unless success?
243
+
222
244
  @meta_data
223
245
  end
224
246
 
247
+
225
248
  # Creates a sendable message from this {Response}.
226
249
  # @return [CZTop::Message} this request packed into a message
227
250
  def to_msg
@@ -229,6 +252,8 @@ module CZTop
229
252
  @status_text, @user_id, @meta_data].map(&:to_s)
230
253
  CZTop::Message.new(fields)
231
254
  end
255
+
232
256
  end
257
+
233
258
  end
234
259
  end