log-courier 1.0.21.ga82ca4c

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.
@@ -0,0 +1,219 @@
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
+ begin
18
+ require 'ffi-rzmq-core'
19
+ require 'ffi-rzmq-core/version'
20
+ require 'ffi-rzmq'
21
+ require 'ffi-rzmq/version'
22
+ rescue LoadError => e
23
+ raise "[LogCourierServer] Could not initialise: #{e}"
24
+ end
25
+
26
+ module LogCourier
27
+ # ZMQ transport implementation for the server
28
+ class ServerZmq
29
+ class ZMQError < StandardError; end
30
+
31
+ attr_reader :port
32
+
33
+ def initialize(options = {})
34
+ @options = {
35
+ logger: nil,
36
+ transport: 'zmq',
37
+ port: 0,
38
+ address: '0.0.0.0',
39
+ curve_secret_key: nil,
40
+ max_packet_size: 10_485_760,
41
+ }.merge!(options)
42
+
43
+ @logger = @options[:logger]
44
+
45
+ libversion = LibZMQ.version
46
+ libversion = "#{libversion[:major]}.#{libversion[:minor]}.#{libversion[:patch]}"
47
+
48
+ if @options[:transport] == 'zmq'
49
+ raise "[LogCourierServer] Transport 'zmq' requires libzmq version >= 4 (the current version is #{libversion})" unless LibZMQ.version4?
50
+
51
+ raise '[LogCourierServer] \'curve_secret_key\' is required' if @options[:curve_secret_key].nil?
52
+
53
+ raise '[LogCourierServer] \'curve_secret_key\' must be a valid 40 character Z85 encoded string' if @options[:curve_secret_key].length != 40 || !z85validate(@options[:curve_secret_key])
54
+ end
55
+
56
+ begin
57
+ @context = ZMQ::Context.new
58
+ # Router so we can send multiple responses
59
+ @socket = @context.socket(ZMQ::ROUTER)
60
+
61
+ if @options[:transport] == 'zmq'
62
+ rc = @socket.setsockopt(ZMQ::CURVE_SERVER, 1)
63
+ raise ZMQError, 'setsockopt CURVE_SERVER failure: ' + ZMQ::Util.error_string unless ZMQ::Util.resultcode_ok?(rc)
64
+
65
+ rc = @socket.setsockopt(ZMQ::CURVE_SECRETKEY, @options[:curve_secret_key])
66
+ raise ZMQError, 'setsockopt CURVE_SECRETKEY failure: ' + ZMQ::Util.error_string unless ZMQ::Util.resultcode_ok?(rc)
67
+ end
68
+
69
+ bind = 'tcp://' + @options[:address] + (@options[:port] == 0 ? ':*' : ':' + @options[:port].to_s)
70
+ rc = @socket.bind(bind)
71
+ raise ZMQError, 'failed to bind at ' + bind + ': ' + rZMQ::Util.error_string unless ZMQ::Util.resultcode_ok?(rc)
72
+
73
+ # Lookup port number that was allocated in case it was set to 0
74
+ endpoint = ''
75
+ rc = @socket.getsockopt(ZMQ::LAST_ENDPOINT, endpoint)
76
+ raise ZMQError, 'getsockopt LAST_ENDPOINT failure: ' + ZMQ::Util.error_string unless ZMQ::Util.resultcode_ok?(rc) && %r{\Atcp://(?:.*):(?<endpoint_port>\d+)\0\z} =~ endpoint
77
+ @port = endpoint_port.to_i
78
+
79
+ @poller = ZMQ::Poller.new
80
+
81
+ if @options[:port] == 0
82
+ @logger.warn '[LogCourierServer] Transport ' + @options[:transport] + ' is listening on ephemeral port ' + @port.to_s
83
+ end
84
+ rescue => e
85
+ raise "[LogCourierServer] Failed to initialise: #{e}"
86
+ end
87
+
88
+ @logger.info "[LogCourierServer] libzmq version #{libversion}"
89
+ @logger.info "[LogCourierServer] ffi-rzmq-core version #{LibZMQ::VERSION}"
90
+ @logger.info "[LogCourierServer] ffi-rzmq version #{ZMQ.version}"
91
+
92
+ # TODO: Implement workers option by receiving on a ROUTER and proxying to a DEALER, with workers connecting to the DEALER
93
+
94
+ reset_timeout
95
+ end
96
+
97
+ def z85validate(z85)
98
+ # ffi-rzmq does not implement decode - but we want to validate during startup
99
+ decoded = FFI::MemoryPointer.from_string(' ' * (8 * z85.length / 10))
100
+ ret = LibZMQ.zmq_z85_decode decoded, z85
101
+ return false if ret.nil?
102
+
103
+ true
104
+ end
105
+
106
+ def run(&block)
107
+ loop do
108
+ begin
109
+ begin
110
+ # Try to receive a message
111
+ data = []
112
+ rc = @socket.recv_strings(data, ZMQ::DONTWAIT)
113
+ unless ZMQ::Util.resultcode_ok?(rc)
114
+ raise ZMQError, 'recv_string error: ' + ZMQ::Util.error_string if ZMQ::Util.errno != ZMQ::EAGAIN
115
+
116
+ # Wait for a message to arrive, handling timeouts
117
+ @poller.deregister @socket, ZMQ::POLLIN | ZMQ::POLLOUT
118
+ @poller.register @socket, ZMQ::POLLIN
119
+ while @poller.poll(1_000) == 0
120
+ raise TimeoutError if Time.now.to_i >= @timeout
121
+ end
122
+ next
123
+ end
124
+ rescue ZMQError => e
125
+ @logger.warn "[LogCourierServer] ZMQ recv_string failed: #{e}" unless @logger.nil?
126
+ next
127
+ end
128
+
129
+ # Pre-send the routing information and remove it from data
130
+ data.delete_if do |msg|
131
+ reset_timeout
132
+ send_with_poll msg, true
133
+ if ZMQ::Util.errno != ZMQ::EAGAIN
134
+ @logger.warn "[LogCourierServer] Message send failed: #{ZMQ::Util.error_string}" unless @logger.nil?
135
+ raise TimeoutError
136
+ end
137
+ break if msg == ""
138
+ true
139
+ end
140
+ data.shift
141
+
142
+ if data.length != 1
143
+ @logger.warn '[LogCourierServer] Invalid message: multipart unexpected' unless @logger.nil?
144
+ else
145
+ recv(data.first, &block)
146
+ end
147
+ rescue TimeoutError
148
+ # We'll let ZeroMQ manage reconnections and new connections
149
+ # There is no point in us doing any form of reconnect ourselves
150
+ # We will keep this timeout in however, for shutdown checks
151
+ reset_timeout
152
+ next
153
+ end
154
+ end
155
+ rescue ShutdownSignal
156
+ # Shutting down
157
+ @logger.warn('[LogCourierServer] Server shutting down') unless @logger.nil?
158
+ rescue => e
159
+ # Some other unknown problem
160
+ @logger.warn("[LogCourierServer] Unknown error: #{e}") unless @logger.nil?
161
+ @logger.warn("[LogCourierServer] #{e.backtrace}: #{e.message} (#{e.class})") unless @logger.nil?
162
+ ensure
163
+ @socket.close
164
+ @context.terminate
165
+ end
166
+
167
+ def recv(data)
168
+ if data.length < 8
169
+ @logger.warn '[LogCourierServer] Invalid message: not enough data' unless @logger.nil?
170
+ return
171
+ end
172
+
173
+ # Unpack the header
174
+ signature, length = data.unpack('A4N')
175
+
176
+ # Verify length
177
+ if data.length - 8 != length
178
+ @logger.warn "[LogCourierServer] Invalid message: data has invalid length (#{data.length - 8} != #{length})" unless @logger.nil?
179
+ return
180
+ elsif length > @options[:max_packet_size]
181
+ @logger.warn "[LogCourierServer] Invalid message: packet too large (#{length} > #{@options[:max_packet_size]})" unless @logger.nil?
182
+ return
183
+ end
184
+
185
+ # Yield the parts
186
+ yield signature, data[8, length], self
187
+ end
188
+
189
+ def send(signature, message)
190
+ reset_timeout
191
+ data = signature + [message.length].pack('N') + message
192
+ send_with_poll data
193
+ end
194
+
195
+ def send_with_poll(data, more = false)
196
+ loop do
197
+ # Try to send a message but never block
198
+ rc = @socket.send_string(data, (more ? ZMQ::SNDMORE : 0) | ZMQ::DONTWAIT)
199
+ break if ZMQ::Util.resultcode_ok?(rc)
200
+ if ZMQ::Util.errno != ZMQ::EAGAIN
201
+ @logger.warn "[LogCourierServer] Message send failed: #{ZMQ::Util.error_string}" unless @logger.nil?
202
+ raise TimeoutError
203
+ end
204
+
205
+ # Wait for send to become available, handling timeouts
206
+ @poller.deregister @socket, ZMQ::POLLIN | ZMQ::POLLOUT
207
+ @poller.register @socket, ZMQ::POLLOUT
208
+ while @poller.poll(1_000) == 0
209
+ raise TimeoutError if Time.now.to_i >= @timeout
210
+ end
211
+ end
212
+ end
213
+
214
+ def reset_timeout()
215
+ # TODO: Make configurable?
216
+ @timeout = Time.now.to_i + 1_800
217
+ end
218
+ end
219
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: log-courier
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.21.ga82ca4c
5
+ platform: ruby
6
+ authors:
7
+ - Jason Woods
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ffi-rzmq
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: multi_json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ description: Log Courier library
42
+ email:
43
+ - devel@jasonwoods.me.uk
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - lib/log-courier/client.rb
49
+ - lib/log-courier/client_tls.rb
50
+ - lib/log-courier/event_queue.rb
51
+ - lib/log-courier/server.rb
52
+ - lib/log-courier/server_tcp.rb
53
+ - lib/log-courier/server_zmq.rb
54
+ homepage: https://github.com/driskell/log-courier
55
+ licenses:
56
+ - Apache
57
+ metadata: {}
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - '>'
70
+ - !ruby/object:Gem::Version
71
+ version: 1.3.1
72
+ requirements: []
73
+ rubyforge_project: nowarning
74
+ rubygems_version: 2.4.2
75
+ signing_key:
76
+ specification_version: 4
77
+ summary: Receive events from Log Courier and transmit between LogStash instances
78
+ test_files: []