riemann-client 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 971fad804164c0db71b3e689d9b866ad89a20e41
4
+ data.tar.gz: 1e7f35daae4ab58fa8018225959da905bfd5f77b
5
+ SHA512:
6
+ metadata.gz: 904df6b31de7feda767274d2a776040287d2b3849280257abd5b167171adf990812b5deabfdde8a48a220b329564476c93a844bb195a11655736578784c84a3c
7
+ data.tar.gz: 3ccaa03c50e24502eee6e9e440279a1d5989b776f70ad04536654e3c887c3f14fb841502593fb84d3dbd9256749a7babb6870d4a7f4f9eb1c259f23f39b5e14f
@@ -18,14 +18,28 @@ class Riemann::Client
18
18
  require 'riemann/client/tcp'
19
19
  require 'riemann/client/udp'
20
20
 
21
- attr_accessor :host, :port, :tcp, :udp, :timeout
21
+ attr_reader :tcp, :udp
22
22
 
23
23
  def initialize(opts = {})
24
- @host = opts[:host] || HOST
25
- @port = opts[:port] || PORT
26
- @timeout = opts[:timeout] || TIMEOUT
27
- @udp = UDP.new opts
28
- @tcp = TCP.new opts
24
+ @options = opts.dup
25
+ @options[:host] ||= HOST
26
+ @options[:port] ||= PORT
27
+ @options[:timeout] ||= TIMEOUT
28
+
29
+ @udp = UDP.new(@options)
30
+ @tcp = TCP.new(@options)
31
+ end
32
+
33
+ def host
34
+ @options[:host]
35
+ end
36
+
37
+ def port
38
+ @options[:port]
39
+ end
40
+
41
+ def timeout
42
+ @options[:timeout]
29
43
  end
30
44
 
31
45
  # Send a state
@@ -1,24 +1,50 @@
1
+ require 'monitor'
2
+ require 'riemann/client/tcp_socket'
3
+
1
4
  module Riemann
2
5
  class Client
3
6
  class TCP < Client
4
7
  attr_accessor :host, :port, :socket
5
8
 
6
- def initialize(opts = {})
7
- @host = opts[:host] || HOST
8
- @port = opts[:port] || PORT
9
- @timeout = opts[:timeout] || TIMEOUT
10
- @locket = Mutex.new
9
+ # Public: Set a socket factory -- an object responding
10
+ # to #call(options) that returns a Socket object
11
+ def self.socket_factory=(factory)
12
+ @socket_factory = factory
13
+ end
14
+
15
+ # Public: Return a socket factory
16
+ def self.socket_factory
17
+ @socket_factory || proc { |options| TcpSocket.connect(options) }
18
+ end
19
+
20
+ def initialize(options = {})
21
+ @options = options
22
+ @locket = Monitor.new
11
23
  end
12
24
 
13
- def connect
14
- Timeout::timeout(@timeout) do
15
- @socket = TCPSocket.new(@host, @port)
25
+ def socket
26
+ @locket.synchronize do
27
+ if @pid && @pid != Process.pid
28
+ close
29
+ end
30
+
31
+ if @socket and not @socket.closed?
32
+ return @socket
33
+ end
34
+
35
+ @socket = self.class.socket_factory.call(@options)
36
+ @pid = Process.pid
37
+
38
+ return @socket
16
39
  end
17
40
  end
18
41
 
19
42
  def close
20
43
  @locket.synchronize do
21
- @socket.close
44
+ if @socket && !@socket.closed?
45
+ @socket.close
46
+ end
47
+ @socket = nil
22
48
  end
23
49
  end
24
50
 
@@ -51,8 +77,8 @@ module Riemann
51
77
 
52
78
  def send_recv(message)
53
79
  with_connection do |s|
54
- s << message.encode_with_length
55
- read_message s
80
+ s.write(message.encode_with_length)
81
+ read_message(s)
56
82
  end
57
83
  end
58
84
 
@@ -65,25 +91,14 @@ module Riemann
65
91
  @locket.synchronize do
66
92
  begin
67
93
  tries += 1
68
- yield(@socket || connect)
69
- rescue IOError => e
70
- raise if tries > 3
71
- connect and retry
72
- rescue Errno::EPIPE => e
73
- raise if tries > 3
74
- connect and retry
75
- rescue Errno::ECONNREFUSED => e
94
+ yield(socket)
95
+ rescue IOError, Errno::EPIPE, Errno::ECONNREFUSED, InvalidResponse, Timeout::Error, Riemann::Client::TcpSocket::Error
96
+ close
76
97
  raise if tries > 3
77
- connect and retry
78
- rescue Errno::ECONNRESET => e
79
- raise if tries > 3
80
- connect and retry
81
- rescue InvalidResponse => e
82
- raise if tries > 3
83
- connect and retry
84
- rescue Timeout::Error => e
85
- raise if tries > 3
86
- connect and retry
98
+ retry
99
+ rescue Exception
100
+ close
101
+ raise
87
102
  end
88
103
  end
89
104
  end
@@ -0,0 +1,334 @@
1
+ require 'socket'
2
+ require 'fcntl'
3
+
4
+ module Riemann
5
+ class Client
6
+ # Socket: A specialized socket that has been configure
7
+ class TcpSocket
8
+ class Error < Riemann::Client::Error; end
9
+ class Timeout < Error; end
10
+
11
+ # Internal:
12
+ # The timeout for reading in seconds. Defaults to 2
13
+ attr_accessor :read_timeout
14
+
15
+ # Internal:
16
+ # The timeout for connecting in seconds. Defaults to 2
17
+ attr_reader :connect_timeout
18
+
19
+ # Internal:
20
+ # The timeout for writing in seconds. Defaults to 2
21
+ attr_reader :write_timeout
22
+
23
+ # Internal:
24
+ # The host this socket is connected to
25
+ attr_reader :host
26
+
27
+ # Internal:
28
+ # The port this socket is connected to
29
+ attr_reader :port
30
+
31
+ # Internal
32
+ #
33
+ # Used for setting TCP_KEEPIDLE: overrides tcp_keepalive_time for a single
34
+ # socket.
35
+ #
36
+ # http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html
37
+ #
38
+ # tcp_keepalive_time:
39
+ #
40
+ # The interval between the last data packet sent (simple ACKs are not
41
+ # considered data) and the first keepalive probe; after the connection is
42
+ # marked to need keepalive, this counter is not used any further.
43
+ attr_reader :keepalive_idle
44
+
45
+ # Internal
46
+ #
47
+ # Used for setting TCP_KEEPINTVL: overrides tcp_keepalive_intvl for a single
48
+ # socket.
49
+ #
50
+ # http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html
51
+ #
52
+ # tcp_keepalive_intvl:
53
+ #
54
+ # The interval between subsequential keepalive probes, regardless of what
55
+ # the connection has exchanged in the meantime.
56
+ attr_reader :keepalive_interval
57
+
58
+ # Internal
59
+ #
60
+ # Used for setting TCP_KEEPCNT: overrides tcp_keepalive_probes for a single
61
+ # socket.
62
+ #
63
+ # http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html
64
+ #
65
+ # tcp_keepalive_probes:
66
+ #
67
+ # The number of unacknowledged probes to send before considering the
68
+ # connection dead and notifying the application layer.
69
+ attr_reader :keepalive_count
70
+
71
+
72
+ # Internal: Create and connect to the given location.
73
+ #
74
+ # options, same as Constructor
75
+ #
76
+ # Returns an instance of KJess::Socket
77
+ def self.connect(options = {})
78
+ s = new(options)
79
+ s.connect
80
+ return s
81
+ end
82
+
83
+ # Internal: Creates a new KJess::Socket
84
+ def initialize( options = {} )
85
+ @host = options[:host]
86
+ @port = options[:port]
87
+
88
+ @connect_timeout = options[:connect_timeout] || options[:timeout] || 2
89
+ @read_timeout = options[:read_timeout] || options[:timeout] || 2
90
+ @write_timeout = options[:write_timeout] || options[:timeout] || 2
91
+
92
+ @keepalive_active = options.fetch(:keepalive_active, true)
93
+ @keepalive_idle = options[:keepalive_idle] || 60
94
+ @keepalive_interval = options[:keepalive_interval] || 30
95
+ @keepalive_count = options[:keepalive_count] || 5
96
+
97
+ @socket = nil
98
+ end
99
+
100
+ # Internal: Return whether or not the keepalive_active flag is set.
101
+ def keepalive_active?
102
+ @keepalive_active
103
+ end
104
+
105
+ # Internal: Low level socket allocation and option configuration
106
+ #
107
+ # Using the options from the initializer, a new ::Socket is created that
108
+ # is:
109
+ #
110
+ # TCP, IPv4 only, autoclosing on exit, nagle's algorithm is disabled and has
111
+ # TCP Keepalive options set if keepalive is supported.
112
+ #
113
+ # Returns a new ::Socket instance
114
+ def blank_socket
115
+ sock = ::Socket.new(::Socket::AF_INET, ::Socket::SOCK_STREAM, 0)
116
+
117
+ # close file descriptors if we exec
118
+ sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
119
+
120
+ # Disable Nagle's algorithm
121
+ sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
122
+
123
+ if using_keepalive? then
124
+ sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE , true)
125
+ sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE , keepalive_idle)
126
+ sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPINTVL, keepalive_interval)
127
+ sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT , keepalive_count)
128
+ end
129
+
130
+ return sock
131
+ end
132
+
133
+ # Internal: Return the connected raw Socket.
134
+ #
135
+ # If the socket is closed or non-existent it will create and connect again.
136
+ #
137
+ # Returns a ::Socket
138
+ def socket
139
+ return @socket unless closed?
140
+ @socket ||= connect()
141
+ end
142
+
143
+ # Internal: Closes the internal ::Socket
144
+ #
145
+ # Returns nothing
146
+ def close
147
+ @socket.close unless closed?
148
+ @socket = nil
149
+ end
150
+
151
+ # Internal: Return true the socket is closed.
152
+ def closed?
153
+ return true if @socket.nil?
154
+ return true if @socket.closed?
155
+ return false
156
+ end
157
+
158
+ # Internal:
159
+ #
160
+ # Connect to the remote host in a non-blocking fashion.
161
+ #
162
+ # Raise Error if there is a failure connecting.
163
+ #
164
+ # Return the ::Socket on success
165
+ def connect
166
+ # Calculate our timeout deadline
167
+ deadline = Time.now.to_f + connect_timeout
168
+
169
+ # Lookup destination address, we only want IPv4 , TCP
170
+ addrs = ::Socket.getaddrinfo(host, port, ::Socket::AF_INET, ::Socket::SOCK_STREAM )
171
+ errors = []
172
+ conn_error = lambda { raise errors.first }
173
+ sock = nil
174
+
175
+ addrs.find( conn_error ) do |addr|
176
+ sock = connect_or_error( addr, deadline, errors )
177
+ end
178
+ return sock
179
+ end
180
+
181
+ # Internal: Connect to the destination or raise an error.
182
+ #
183
+ # Connect to the address or capture the error of the connection
184
+ #
185
+ # addr - An address returned from Socket.getaddrinfo()
186
+ # deadline - the after which we should raise a timeout error
187
+ # errors - a collection of errors to append an error too should we have one.
188
+ #
189
+ # Make an attempt to connect to the given address. If it is successful,
190
+ # return the socket.
191
+ #
192
+ # Should the connection fail, append the exception to the errors array and
193
+ # return false.
194
+ #
195
+ def connect_or_error( addr, deadline, errors )
196
+ timeout = deadline - Time.now.to_f
197
+ raise Timeout, "Could not connect to #{host}:#{port}" if timeout <= 0
198
+ return connect_nonblock( addr, timeout )
199
+ rescue Error => e
200
+ errors << e
201
+ return false
202
+ end
203
+
204
+ # Internal: Connect to the give address within the timeout.
205
+ #
206
+ # Make an attempt to connect to a single address within the given timeout.
207
+ #
208
+ # Return the ::Socket when it is connected, or raise an Error if no
209
+ # connection was possible.
210
+ def connect_nonblock( addr, timeout )
211
+ sockaddr = ::Socket.pack_sockaddr_in(addr[1], addr[3])
212
+ sock = blank_socket()
213
+ sock.connect_nonblock( sockaddr )
214
+ return sock
215
+ rescue Errno::EINPROGRESS
216
+ if IO.select(nil, [sock], nil, timeout).nil?
217
+ sock.close rescue nil
218
+ raise Timeout, "Could not connect to #{host}:#{port} within #{timeout} seconds"
219
+ end
220
+ return connect_nonblock_finalize( sock, sockaddr )
221
+ rescue => ex
222
+ sock.close rescue nil
223
+ raise Error, "Could not connect to #{host}:#{port}: #{ex.class}: #{ex.message}", ex.backtrace
224
+ end
225
+
226
+
227
+ # Internal: Make sure that a non-blocking connect has truely connected.
228
+ #
229
+ # Ensure that the given socket is actually connected to the given adddress.
230
+ #
231
+ # Returning the socket if it is and raising an Error if it isn't.
232
+ def connect_nonblock_finalize( sock, sockaddr )
233
+ sock.connect_nonblock( sockaddr )
234
+ return sock
235
+ rescue Errno::EISCONN
236
+ return sock
237
+ rescue => ex
238
+ sock.close rescue nil
239
+ raise Error, "Could not connect to #{host}:#{port}: #{ex.class}: #{ex.message}", ex.backtrace
240
+ end
241
+
242
+ # Internal: say if we are using TCP Keep Alive or not
243
+ #
244
+ # We will return true if the initialization options :keepalive_active is
245
+ # set to true, and if all the constants that are necessary to use TCP keep
246
+ # alive are defined.
247
+ #
248
+ # It may be the case that on some operating systems that the constants are
249
+ # not defined, so in that case we do not want to attempt to use tcp keep
250
+ # alive if we are unable to do so in any case.
251
+ #
252
+ # Returns true or false
253
+ def using_keepalive?
254
+ using = false
255
+ if keepalive_active? then
256
+ using = [ :SOL_SOCKET, :SO_KEEPALIVE, :SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all? do |c|
257
+ ::Socket.const_defined? c
258
+ end
259
+ end
260
+ return using
261
+ end
262
+
263
+ # Reads length bytes from the socket
264
+ #
265
+ # length - the number of bytes to read from the socket
266
+ # outbuf - an optional buffer to store the bytes in
267
+ #
268
+ # Returns the bytes read if no outbuf is specified
269
+ def read(length, outbuf = nil)
270
+ if outbuf
271
+ outbuf.replace('')
272
+ buf = outbuf
273
+ else
274
+ buf = ''
275
+ end
276
+
277
+ while buf.length < length
278
+ unless rb = readpartial(length - buf.length)
279
+ break
280
+ end
281
+
282
+ buf << rb
283
+ end
284
+
285
+ return buf
286
+ end
287
+
288
+ # Internal: Read up to a maxlen of data from the socket and store it in outbuf
289
+ #
290
+ # maxlen - the maximum number of bytes to read from the socket
291
+ # outbuf - the buffer in which to store the bytes.
292
+ #
293
+ # Returns the bytes read
294
+ def readpartial(maxlen, outbuf = nil)
295
+ return socket.read_nonblock(maxlen, outbuf)
296
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNRESET
297
+ if wait_readable(read_timeout)
298
+ retry
299
+ else
300
+ raise Timeout, "Could not read from #{host}:#{port} in #{read_timeout} seconds"
301
+ end
302
+ end
303
+
304
+ # Internal: Write the given data to the socket
305
+ #
306
+ # buf - the data to write to the socket.
307
+ #
308
+ # Raises an error if it is unable to write the data to the socket within the
309
+ # write_timeout.
310
+ #
311
+ # returns nothing
312
+ def write(buf)
313
+ until buf.nil? or (buf.length == 0) do
314
+ written = socket.write_nonblock(buf)
315
+ buf = buf[written, buf.length]
316
+ end
317
+ rescue Errno::EWOULDBLOCK, Errno::EINTR, Errno::EAGAIN, Errno::ECONNRESET
318
+ if wait_writable(write_timeout)
319
+ retry
320
+ else
321
+ raise Timeout, "Could not write to #{host}:#{port} in #{write_timeout} seconds"
322
+ end
323
+ end
324
+
325
+ def wait_writable(timeout = nil)
326
+ IO.select(nil, [@socket], nil, timeout || write_timeout)
327
+ end
328
+
329
+ def wait_readable(timeout = nil)
330
+ IO.select([@socket], nil, nil, timeout || read_timeout)
331
+ end
332
+ end
333
+ end
334
+ end
@@ -1,3 +1,3 @@
1
1
  module Riemann
2
- VERSION = '0.2.2'
2
+ VERSION = '0.2.3'
3
3
  end
metadata CHANGED
@@ -1,62 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riemann-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
5
- prerelease:
4
+ version: 0.2.3
6
5
  platform: ruby
7
6
  authors:
8
7
  - Kyle Kingsbury
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-05-28 00:00:00.000000000 Z
11
+ date: 2014-04-30 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: beefcake
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: 0.3.5
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: 0.3.5
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: trollop
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: 1.16.2
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: 1.16.2
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: mtrc
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: 0.0.4
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: 0.0.4
62
55
  description:
@@ -70,6 +63,7 @@ files:
70
63
  - lib/riemann/auto_state.rb
71
64
  - lib/riemann/client.rb
72
65
  - lib/riemann/client/tcp.rb
66
+ - lib/riemann/client/tcp_socket.rb
73
67
  - lib/riemann/client/udp.rb
74
68
  - lib/riemann/event.rb
75
69
  - lib/riemann/message.rb
@@ -81,26 +75,25 @@ files:
81
75
  - README.markdown
82
76
  homepage: https://github.com/aphyr/riemann-ruby-client
83
77
  licenses: []
78
+ metadata: {}
84
79
  post_install_message:
85
80
  rdoc_options: []
86
81
  require_paths:
87
82
  - lib
88
83
  required_ruby_version: !ruby/object:Gem::Requirement
89
- none: false
90
84
  requirements:
91
- - - ! '>='
85
+ - - '>='
92
86
  - !ruby/object:Gem::Version
93
87
  version: 1.8.7
94
88
  required_rubygems_version: !ruby/object:Gem::Requirement
95
- none: false
96
89
  requirements:
97
- - - ! '>='
90
+ - - '>='
98
91
  - !ruby/object:Gem::Version
99
92
  version: '0'
100
93
  requirements: []
101
94
  rubyforge_project: riemann-client
102
- rubygems_version: 1.8.25
95
+ rubygems_version: 2.0.14
103
96
  signing_key:
104
- specification_version: 3
97
+ specification_version: 4
105
98
  summary: Client for the distributed event system Riemann.
106
99
  test_files: []