log-courier 1.9.0 → 2.7.2

Sign up to get free protection for your applications and to get access to all the features.
metadata CHANGED
@@ -1,57 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: log-courier
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.0
4
+ version: 2.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Woods
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-10 00:00:00.000000000 Z
11
+ date: 2021-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: cabin
15
- version_requirements: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '0.6'
20
14
  requirement: !ruby/object:Gem::Requirement
21
15
  requirements:
22
16
  - - "~>"
23
17
  - !ruby/object:Gem::Version
24
18
  version: '0.6'
25
- prerelease: false
19
+ name: cabin
26
20
  type: :runtime
27
- - !ruby/object:Gem::Dependency
28
- name: ffi-rzmq
21
+ prerelease: false
29
22
  version_requirements: !ruby/object:Gem::Requirement
30
23
  requirements:
31
24
  - - "~>"
32
25
  - !ruby/object:Gem::Version
33
- version: '2.0'
26
+ version: '0.6'
27
+ - !ruby/object:Gem::Dependency
34
28
  requirement: !ruby/object:Gem::Requirement
35
29
  requirements:
36
30
  - - "~>"
37
31
  - !ruby/object:Gem::Version
38
- version: '2.0'
39
- prerelease: false
40
- type: :runtime
41
- - !ruby/object:Gem::Dependency
32
+ version: '1.10'
42
33
  name: multi_json
34
+ type: :runtime
35
+ prerelease: false
43
36
  version_requirements: !ruby/object:Gem::Requirement
44
37
  requirements:
45
38
  - - "~>"
46
39
  - !ruby/object:Gem::Version
47
40
  version: '1.10'
48
- requirement: !ruby/object:Gem::Requirement
49
- requirements:
50
- - - "~>"
51
- - !ruby/object:Gem::Version
52
- version: '1.10'
53
- prerelease: false
54
- type: :runtime
55
41
  description: Log Courier library
56
42
  email:
57
43
  - devel@jasonwoods.me.uk
@@ -62,13 +48,12 @@ files:
62
48
  - lib/log-courier/client.rb
63
49
  - lib/log-courier/client_tcp.rb
64
50
  - lib/log-courier/event_queue.rb
51
+ - lib/log-courier/protocol.rb
65
52
  - lib/log-courier/server.rb
66
53
  - lib/log-courier/server_tcp.rb
67
- - lib/log-courier/server_zmq.rb
68
- - lib/log-courier/zmq_qpoll.rb
69
54
  homepage: https://github.com/driskell/ruby-log-courier
70
55
  licenses:
71
- - Apache
56
+ - Apache-2.0
72
57
  metadata: {}
73
58
  post_install_message:
74
59
  rdoc_options: []
@@ -85,8 +70,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
85
70
  - !ruby/object:Gem::Version
86
71
  version: '0'
87
72
  requirements: []
88
- rubyforge_project: nowarning
89
- rubygems_version: 2.4.8
73
+ rubygems_version: 3.0.6
90
74
  signing_key:
91
75
  specification_version: 4
92
76
  summary: Ruby implementation of the Courier protocol
@@ -1,400 +0,0 @@
1
- # encoding: utf-8
2
-
3
- # Copyright 2014 Jason Woods.
4
- #
5
- # Licensed under the Apache License, Version 2.0 (the "License");
6
- # you may not use this file except in compliance with the License.
7
- # You may obtain a copy of the License at
8
- #
9
- # http://www.apache.org/licenses/LICENSE-2.0
10
- #
11
- # Unless required by applicable law or agreed to in writing, software
12
- # distributed under the License is distributed on an "AS IS" BASIS,
13
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- # See the License for the specific language governing permissions and
15
- # limitations under the License.
16
-
17
- require 'thread'
18
- require 'log-courier/zmq_qpoll'
19
-
20
- module LogCourier
21
- # ZMQ transport implementation for the server
22
- class ServerZmq
23
- class ZMQError < StandardError; end
24
-
25
- class << self
26
- @print_zmq_versions = false
27
-
28
- def print_zmq_versions(logger)
29
- return if @print_zmq_versions || logger.nil?
30
-
31
- libversion = LibZMQ.version
32
- libversion = "#{libversion[:major]}.#{libversion[:minor]}.#{libversion[:patch]}"
33
-
34
- logger.info 'libzmq', :version => libversion
35
- logger.info 'ffi-rzmq-core', :version => LibZMQ::VERSION
36
- logger.info 'ffi-rzmq', :version => ZMQ.version
37
-
38
- @print_zmq_versions = true
39
- end
40
- end
41
-
42
- attr_reader :port
43
-
44
- def initialize(options = {})
45
- @options = {
46
- logger: nil,
47
- transport: 'zmq',
48
- port: 0,
49
- address: '0.0.0.0',
50
- curve_secret_key: nil,
51
- max_packet_size: 10_485_760,
52
- peer_recv_queue: 10,
53
- }.merge!(options)
54
-
55
- @logger = @options[:logger]
56
-
57
- self.class.print_zmq_versions @logger
58
-
59
- if @options[:transport] == 'zmq'
60
- fail "input/courier: Transport 'zmq' requires libzmq version >= 4" unless LibZMQ.version4?
61
-
62
- fail 'input/courier: \'curve_secret_key\' is required' if @options[:curve_secret_key].nil?
63
-
64
- fail 'input/courier: \'curve_secret_key\' must be a valid 40 character Z85 encoded string' if @options[:curve_secret_key].length != 40 || !z85validate(@options[:curve_secret_key])
65
- end
66
-
67
- begin
68
- @context = ZMQ::Context.new
69
- fail ZMQError, 'context creation error: ' + ZMQ::Util.error_string if @context.nil?
70
-
71
- # Router so we can send multiple responses
72
- @socket = @context.socket(ZMQ::ROUTER)
73
- fail ZMQError, 'socket creation error: ' + ZMQ::Util.error_string if @socket.nil?
74
-
75
- rc = @socket.setsockopt(ZMQ::LINGER, 0)
76
- fail ZMQError, 'setsockopt LINGER failure: ' + ZMQ::Util.error_string unless ZMQ::Util.resultcode_ok?(rc)
77
-
78
- if @options[:transport] == 'zmq'
79
- rc = @socket.setsockopt(ZMQ::CURVE_SERVER, 1)
80
- fail 'setsockopt CURVE_SERVER failure: ' + ZMQ::Util.error_string unless ZMQ::Util.resultcode_ok?(rc)
81
-
82
- rc = @socket.setsockopt(ZMQ::CURVE_SECRETKEY, @options[:curve_secret_key])
83
- fail 'setsockopt CURVE_SECRETKEY failure: ' + ZMQ::Util.error_string unless ZMQ::Util.resultcode_ok?(rc)
84
- end
85
-
86
- bind = 'tcp://' + @options[:address] + (@options[:port] == 0 ? ':*' : ':' + @options[:port].to_s)
87
- rc = @socket.bind(bind)
88
- fail 'failed to bind at ' + bind + ': ' + ZMQ::Util.error_string unless ZMQ::Util.resultcode_ok?(rc)
89
-
90
- # Lookup port number that was allocated in case it was set to 0
91
- endpoint = ''
92
- rc = @socket.getsockopt(ZMQ::LAST_ENDPOINT, endpoint)
93
- fail 'getsockopt LAST_ENDPOINT failure: ' + ZMQ::Util.error_string unless ZMQ::Util.resultcode_ok?(rc) && %r{\Atcp://(?:.*):(?<endpoint_port>\d+)\0\z} =~ endpoint
94
- @port = endpoint_port.to_i
95
-
96
- if @options[:port] == 0
97
- @logger.warn 'Ephemeral port allocated', :transport => @options[:transport], :port => @port unless @logger.nil?
98
- end
99
- rescue => e
100
- raise "input/courier: Failed to initialise: #{e}"
101
- end
102
-
103
- # TODO: Implement workers option by receiving on a ROUTER and proxying to a DEALER, with workers connecting to the DEALER
104
-
105
- # TODO: Make this send queue configurable?
106
- @send_queue = EventQueue.new 2
107
- @factory = ClientFactoryZmq.new(@options, @send_queue)
108
-
109
- # Setup poller
110
- @poller = ZMQPoll::ZMQPoll.new(@context)
111
- @poller.register_socket @socket, ZMQ::POLLIN
112
- @poller.register_queue_to_socket @send_queue, @socket
113
- end
114
-
115
- def run(&block)
116
- errors = 0
117
- loop do
118
- begin
119
- @poller.poll(5_000) do |socket, r, w|
120
- next if socket != @socket
121
- next if !r
122
-
123
- receive &block
124
- end
125
- errors = 0
126
- rescue ZMQPoll::ZMQError => e
127
- @logger.warn e, :hint => 'ZMQ poll failure' unless @logger.nil?
128
- next
129
- rescue ZMQPoll::ZMQTerm
130
- # Fall into shutdown signal, context was terminated
131
- # This can happen in JRuby - it seems to run finalisers too early
132
- fail ShutdownSignal
133
- rescue ZMQPoll::TimeoutError
134
- # We'll let ZeroMQ manage reconnections and new connections
135
- # There is no point in us doing any form of reconnect ourselves
136
- next
137
- end
138
- end
139
- return
140
- rescue ShutdownSignal
141
- # Shutting down
142
- @logger.warn 'Server shutting down' unless @logger.nil?
143
- return
144
- rescue StandardError, NativeException => e
145
- # Some other unknown problem
146
- @logger.warn e, :hint => 'Unknown error, shutting down' unless @logger.nil?
147
- return
148
- ensure
149
- @poller.shutdown
150
- @factory.shutdown
151
- @socket.close
152
- @context.terminate
153
- end
154
-
155
- private
156
-
157
- def z85validate(z85)
158
- # ffi-rzmq does not implement decode - but we want to validate during startup
159
- decoded = FFI::MemoryPointer.from_string(' ' * (8 * z85.length / 10))
160
- ret = LibZMQ.zmq_z85_decode decoded, z85
161
- return false if ret.nil?
162
- true
163
- end
164
-
165
- def receive(&block)
166
- # Try to receive a message
167
- data = []
168
- rc = @socket.recv_strings(data, ZMQ::DONTWAIT)
169
- unless ZMQ::Util.resultcode_ok?(rc)
170
- fail ZMQError, 'recv_string error: ' + ZMQ::Util.error_string if ZMQ::Util.errno != ZMQ::EAGAIN
171
- end
172
-
173
- # Save the source information that appears before the null messages
174
- source = []
175
- source.push data.shift until data.length == 0 || data[0] == ''
176
-
177
- if data.length == 0
178
- @logger.warn 'Invalid message: no data', :source_length => source.length unless @logger.nil?
179
- return
180
- elsif data.length == 1
181
- @logger.warn 'Invalid message: empty data', :source_length => source.length unless @logger.nil?
182
- return
183
- end
184
-
185
- # Drop the null message separator
186
- data.shift
187
-
188
- if data.length != 1
189
- @logger.warn 'Invalid message: multipart unexpected', :source_length => source.length, :data_length => data.length unless @logger.nil?
190
- if !@logger.nil? && @logger.debug?
191
- i = 0
192
- parts = {}
193
- data.each do |msg|
194
- i += 1
195
- parts[i] = "#{part.length}:[#{msg[0..31].gsub(/[^[:print:]]/, '.')}]"
196
- end
197
- @logger.debug 'Data', parts
198
- end
199
- return
200
- end
201
-
202
- @factory.deliver source, data.first, &block
203
- return
204
- end
205
- end
206
-
207
- class ClientFactoryZmq
208
- attr_reader :options
209
- attr_reader :send_queue
210
-
211
- def initialize(options, send_queue)
212
- @options = options
213
- @logger = @options[:logger]
214
-
215
- @send_queue = send_queue
216
- @index = {}
217
- @client_threads = {}
218
- @mutex = Mutex.new
219
- end
220
-
221
- def shutdown
222
- # Stop other threads from try_drop collisions
223
- client_threads = @mutex.synchronize do
224
- client_threads = @client_threads
225
- @client_threads = {}
226
- client_threads
227
- end
228
-
229
- client_threads.each_value do |thr|
230
- thr.raise ShutdownSignal
231
- end
232
-
233
- client_threads.each_value(&:join)
234
- return
235
- end
236
-
237
- def deliver(source, data, &block)
238
- # Find the handling thread
239
- # We separate each source into threads so that each thread can respond
240
- # with partial ACKs if we hit a slow down
241
- # If we processed in a single thread, we'd only be able to respond to
242
- # a single client with partial ACKs
243
- @mutex.synchronize do
244
- index = @index
245
- source.each do |identity|
246
- index[identity] = {} if !index.key?(identity)
247
- index = index[identity]
248
- end
249
-
250
- if !index.key?('')
251
- source_str = source.map do |s|
252
- s.each_byte.map do |b|
253
- b.to_s(16).rjust(2, '0')
254
- end
255
- end.join
256
-
257
- @logger.info 'New source', :source => source_str unless @logger.nil?
258
-
259
- # Create the client and associated thread
260
- client = ClientZmq.new(self, source, source_str) do
261
- try_drop source, source_str
262
- end
263
-
264
- thread = Thread.new do
265
- client.run &block
266
- end
267
-
268
- @client_threads[thread] = thread
269
-
270
- index[''] = {
271
- 'client' => client,
272
- 'thread' => thread,
273
- }
274
- end
275
-
276
- # Existing thread, throw on the queue, if not enough room (timeout) drop the message
277
- begin
278
- index['']['client'].push data, 0
279
- rescue LogCourier::TimeoutError
280
- # TODO: Log a warning about this?
281
- end
282
- end
283
- return
284
- end
285
-
286
- private
287
-
288
- def try_drop(source, source_str)
289
- # This is called when a client goes idle, to cleanup resources
290
- # We may tie this into zmq monitor
291
- @mutex.synchronize do
292
- index = @index
293
- parents = []
294
- source.each do |identity|
295
- if !index.key?(identity)
296
- @logger.warn 'Unknown idle source failed to shutdown', :source => source_str unless @logger.nil?
297
- break
298
- end
299
- parents.push [index, identity]
300
- index = index[identity]
301
- end
302
-
303
- if !index.key?('')
304
- @logger.warn 'Unknown idle source failed to shutdown', :source => source_str unless @logger.nil?
305
- break
306
- end
307
-
308
- # Don't allow drop if we have messages in the queue
309
- if index['']['client'].length != 0
310
- @logger.warn 'Failed idle source shutdown as message queue is not empty', :source => source_str unless @logger.nil?
311
- return false
312
- end
313
-
314
- @logger.info 'Idle source shutting down', :source => source_str unless @logger.nil?
315
-
316
- # Delete the entry
317
- @client_threads.delete(index['']['thread'])
318
- index.delete('')
319
-
320
- # Cleanup orphaned leafs
321
- parents.reverse_each do |path|
322
- path[0].delete(path[1]) if path[0][path[1]].length == 0
323
- end
324
- end
325
-
326
- return true
327
- end
328
- end
329
-
330
- class ClientZmq < EventQueue
331
- def initialize(factory, source, source_str, &try_drop)
332
- @factory = factory
333
- @logger = @factory.options[:logger]
334
- @send_queue = @factory.send_queue
335
- @source = source
336
- @source_str = source_str
337
- @try_drop = try_drop
338
-
339
- # Setup the queue for receiving events to process
340
- super @factory.options[:peer_recv_queue]
341
- end
342
-
343
- def run(&block)
344
- loop do
345
- begin
346
- # TODO: Make timeout configurable?
347
- data = self.pop(30)
348
- recv(data, &block)
349
- rescue TimeoutError
350
- # Try to clean up resources - if we fail, new messages have arrived
351
- retry if !@try_drop.call(@source)
352
- break
353
- end
354
- end
355
- return
356
- rescue ShutdownSignal
357
- # Shutting down
358
- @logger.info 'Source shutting down', :source => @source_str unless @logger.nil?
359
- return
360
- rescue StandardError, NativeException => e
361
- # Some other unknown problem
362
- @logger.warn e, :hint => 'Unknown error, connection aborted', :source => @source_str unless @logger.nil?
363
- raise e
364
- end
365
-
366
- def send(signature, message)
367
- data = signature + [message.length].pack('N') + message
368
- @send_queue.push @source + ['', data]
369
- return
370
- end
371
-
372
- def add_fields(event)
373
- end
374
-
375
- private
376
-
377
- def recv(data)
378
- if data.length < 8
379
- @logger.warn 'Invalid message: not enough data', :data_length => data.length, :source => @source_str unless @logger.nil?
380
- return
381
- end
382
-
383
- # Unpack the header
384
- signature, length = data.unpack('A4N')
385
-
386
- # Verify length
387
- if data.length - 8 != length
388
- @logger.warn 'Invalid message: data has invalid length', :data_length => data.length - 8, :encoded_length => length, :source => @source_str unless @logger.nil?
389
- return
390
- elsif length > @factory.options[:max_packet_size]
391
- @logger.warn 'Invalid message: packet too large', :size => length, :max_packet_size => @options[:max_packet_size], :source => @source_str unless @logger.nil?
392
- return
393
- end
394
-
395
- # Yield the parts
396
- yield signature, data[8, length], self
397
- return
398
- end
399
- end
400
- end