bunny 1.0.0 → 1.0.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1fc5878c28d1164882b924bba6452f035780026c
4
- data.tar.gz: ba690c50d70f94994cc5ae29a5668a9d956cc89a
3
+ metadata.gz: 0836f4aa9086b7ac58dcfc0f2aa21e65580fca67
4
+ data.tar.gz: e15d6b9d8a1cb73e1bf686eb0fd2dd0c59741452
5
5
  SHA512:
6
- metadata.gz: 43a7f6f137bde88a7b7af3e5ec240ba1e8eafafd819eba044b634570fe15d2ba1f88dcb8c0119df493f5a681ca0e7a4e006809164bd1f20044a8654765015cad
7
- data.tar.gz: a784125f41292894f6003ab1f06b23dbec5077e66e916b027aafc89f169ba8b0f109a5dec2cb555f0effbdd5d124aa0d6b1bf923935087b3eb74eaeb6c293202
6
+ metadata.gz: f63f5f0e9646746806cb21a5a1dc57e11f85bb23f5655937dd30136054996dd518cb91c296262569b6b92be83c5a32b503c6b0ff88f8788a3481d4f1f722891c
7
+ data.tar.gz: 41d514a852463368e4c37a63bda65d8d98308d42fa62f4d4eb925e65554d8e552d29aa8e2b4c1d4386127a34c2f512ee9810a99aeea2793188b5189a586e71c4
@@ -1,3 +1,22 @@
1
+ ## Changes between Bunny 1.0.0 and 1.0.1
2
+
3
+ ### Default CA's Paths Are Disabled on JRuby
4
+
5
+ Bunny uses OpenSSL provided CA certificate paths. This
6
+ caused problems on some platforms on JRuby (see [jruby/jruby#155](https://github.com/jruby/jruby/issues/1055)).
7
+
8
+ To avoid these issues, Bunny no longer uses default CA certificate paths on JRuby
9
+ (there are no changes for other Rubies), so it's necessary to provide
10
+ CA certificate explicitly.
11
+
12
+ ### Fixes CPU Burn on JRuby
13
+
14
+ Bunny now uses slightly different ways of continuously reading from the socket
15
+ on CRuby and JRuby, to prevent abnormally high CPU usage on JRuby after a
16
+ certain period of time (the frequency of `EWOULDBLOCK` being raised spiked
17
+ sharply).
18
+
19
+
1
20
  ## Changes between Bunny 1.0.0.rc2 and 1.0.0.rc3
2
21
 
3
22
  ### [Authentication Failure Notification](http://www.rabbitmq.com/auth-notification.html) Support
@@ -8,7 +8,9 @@ require "amq/protocol/extensions"
8
8
 
9
9
  require "bunny/framing"
10
10
  require "bunny/exceptions"
11
+
11
12
  require "bunny/socket"
13
+
12
14
  require "bunny/timeout"
13
15
 
14
16
  begin
@@ -0,0 +1,83 @@
1
+ require "socket"
2
+
3
+ module Bunny
4
+ # TCP socket extension that uses TCP_NODELAY and supports reading
5
+ # fully.
6
+ #
7
+ # Heavily inspired by Dalli by Mike Perham.
8
+ # @private
9
+ class Socket < TCPSocket
10
+ attr_accessor :options
11
+
12
+ # IO::WaitReadable is 1.9+ only
13
+ READ_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK]
14
+ READ_RETRY_EXCEPTION_CLASSES << IO::WaitReadable if IO.const_defined?(:WaitReadable)
15
+
16
+ def self.open(host, port, options = {})
17
+ Timeout.timeout(options[:socket_timeout], ClientTimeout) do
18
+ sock = new(host, port)
19
+ if Socket.constants.include?('TCP_NODELAY') || Socket.constants.include?(:TCP_NODELAY)
20
+ sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
21
+ end
22
+ sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if options.fetch(:keepalive, true)
23
+ sock.options = {:host => host, :port => port}.merge(options)
24
+ sock
25
+ end
26
+ end
27
+
28
+ # Reads given number of bytes with an optional timeout
29
+ #
30
+ # @param [Integer] count How many bytes to read
31
+ # @param [Integer] timeout Timeout
32
+ #
33
+ # @return [String] Data read from the socket
34
+ # @api public
35
+ def read_fully(count, timeout = nil)
36
+ return nil if @__bunny_socket_eof_flag__
37
+
38
+ value = ''
39
+ begin
40
+ loop do
41
+ value << read_nonblock(count - value.bytesize)
42
+ break if value.bytesize >= count
43
+ end
44
+ rescue EOFError
45
+ # @eof will break Rubinius' TCPSocket implementation. MK.
46
+ @__bunny_socket_eof_flag__ = true
47
+ rescue *READ_RETRY_EXCEPTION_CLASSES
48
+ if IO.select([self], nil, nil, timeout)
49
+ retry
50
+ else
51
+ raise Timeout::Error, "IO timeout when reading #{count} bytes"
52
+ end
53
+ end
54
+ value
55
+ end # read_fully
56
+
57
+ # Writes provided data using IO#write_nonblock, taking care of handling
58
+ # of exceptions it raises when writing would fail (e.g. due to socket buffer
59
+ # being full).
60
+ #
61
+ # IMPORTANT: this method will mutate (slice) the argument. Pass in duplicates
62
+ # if this is not appropriate in your case.
63
+ #
64
+ # @param [String] data Data to write
65
+ #
66
+ # @api public
67
+ def write_nonblock_fully(data)
68
+ return nil if @__bunny_socket_eof_flag__
69
+
70
+ begin
71
+ while !data.empty?
72
+ written = self.write_nonblock(data)
73
+ data.slice!(0, written)
74
+ end
75
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
76
+ IO.select([], [self])
77
+ retry
78
+ end
79
+
80
+ data.bytesize
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,57 @@
1
+ require "socket"
2
+
3
+ module Bunny
4
+ begin
5
+ require "openssl"
6
+
7
+ # TLS-enabled TCP socket that implements convenience
8
+ # methods found in Bunny::Socket.
9
+ class SSLSocket < OpenSSL::SSL::SSLSocket
10
+
11
+ # IO::WaitReadable is 1.9+ only
12
+ READ_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK]
13
+ READ_RETRY_EXCEPTION_CLASSES << IO::WaitReadable if IO.const_defined?(:WaitReadable)
14
+
15
+
16
+ # Reads given number of bytes with an optional timeout
17
+ #
18
+ # @param [Integer] count How many bytes to read
19
+ # @param [Integer] timeout Timeout
20
+ #
21
+ # @return [String] Data read from the socket
22
+ # @api public
23
+ def read_fully(count, timeout = nil)
24
+ return nil if @__bunny_socket_eof_flag__
25
+
26
+ value = ''
27
+ begin
28
+ loop do
29
+ value << read_nonblock(count - value.bytesize)
30
+ break if value.bytesize >= count
31
+ end
32
+ rescue EOFError => e
33
+ @__bunny_socket_eof_flag__ = true
34
+ rescue OpenSSL::SSL::SSLError => e
35
+ if e.message == "read would block"
36
+ if IO.select([self], nil, nil, timeout)
37
+ retry
38
+ else
39
+ raise Timeout::Error, "IO timeout when reading #{count} bytes"
40
+ end
41
+ else
42
+ raise e
43
+ end
44
+ rescue *READ_RETRY_EXCEPTION_CLASSES => e
45
+ if IO.select([self], nil, nil, timeout)
46
+ retry
47
+ else
48
+ raise Timeout::Error, "IO timeout when reading #{count} bytes"
49
+ end
50
+ end
51
+ value
52
+ end
53
+ end
54
+ rescue LoadError => le
55
+ puts "Could not load OpenSSL"
56
+ end
57
+ end
@@ -0,0 +1,40 @@
1
+ require "bunny/cruby/socket"
2
+
3
+ module Bunny
4
+ module JRuby
5
+ # TCP socket extension that uses Socket#readpartial to avoid excessive CPU
6
+ # burn after some time. See issue #165.
7
+ # @private
8
+ class Socket < Bunny::Socket
9
+
10
+ # Reads given number of bytes with an optional timeout
11
+ #
12
+ # @param [Integer] count How many bytes to read
13
+ # @param [Integer] timeout Timeout
14
+ #
15
+ # @return [String] Data read from the socket
16
+ # @api public
17
+ def read_fully(count, timeout = nil)
18
+ return nil if @__bunny_socket_eof_flag__
19
+
20
+ value = ''
21
+ begin
22
+ loop do
23
+ value << readpartial(count - value.bytesize)
24
+ break if value.bytesize >= count
25
+ end
26
+ rescue EOFError
27
+ # @eof will break Rubinius' TCPSocket implementation. MK.
28
+ @__bunny_socket_eof_flag__ = true
29
+ rescue *READ_RETRY_EXCEPTION_CLASSES
30
+ if IO.select([self], nil, nil, timeout)
31
+ retry
32
+ else
33
+ raise Timeout::Error, "IO timeout when reading #{count} bytes"
34
+ end
35
+ end
36
+ value
37
+ end # read_fully
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,53 @@
1
+ module Bunny
2
+ module JRuby
3
+ begin
4
+ require "bunny/cruby/ssl_socket"
5
+ require "openssl"
6
+
7
+ # TLS-enabled TCP socket that implements convenience
8
+ # methods found in Bunny::Socket.
9
+ class SSLSocket < Bunny::SSLSocket
10
+
11
+ # Reads given number of bytes with an optional timeout
12
+ #
13
+ # @param [Integer] count How many bytes to read
14
+ # @param [Integer] timeout Timeout
15
+ #
16
+ # @return [String] Data read from the socket
17
+ # @api public
18
+ def read_fully(count, timeout = nil)
19
+ return nil if @__bunny_socket_eof_flag__
20
+
21
+ value = ''
22
+ begin
23
+ loop do
24
+ value << read_nonblock(count - value.bytesize)
25
+ break if value.bytesize >= count
26
+ end
27
+ rescue EOFError => e
28
+ @__bunny_socket_eof_flag__ = true
29
+ rescue OpenSSL::SSL::SSLError => e
30
+ if e.message == "read would block"
31
+ if IO.select([self], nil, nil, timeout)
32
+ retry
33
+ else
34
+ raise Timeout::Error, "IO timeout when reading #{count} bytes"
35
+ end
36
+ else
37
+ raise e
38
+ end
39
+ rescue *READ_RETRY_EXCEPTION_CLASSES => e
40
+ if IO.select([self], nil, nil, timeout)
41
+ retry
42
+ else
43
+ raise Timeout::Error, "IO timeout when reading #{count} bytes"
44
+ end
45
+ end
46
+ value
47
+ end
48
+ end
49
+ rescue LoadError => le
50
+ puts "Could not load OpenSSL"
51
+ end
52
+ end
53
+ end
@@ -1,83 +1,14 @@
1
- require "socket"
1
+ # See #165. MK.
2
+ if defined?(JRUBY_VERSION)
3
+ require "bunny/jruby/socket"
2
4
 
3
- module Bunny
4
- # TCP socket extension that uses TCP_NODELAY and supports reading
5
- # fully.
6
- #
7
- # Heavily inspired by Dalli by Mike Perham.
8
- # @private
9
- class Socket < TCPSocket
10
- attr_accessor :options
11
-
12
- # IO::WaitReadable is 1.9+ only
13
- READ_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK]
14
- READ_RETRY_EXCEPTION_CLASSES << IO::WaitReadable if IO.const_defined?(:WaitReadable)
15
-
16
- def self.open(host, port, options = {})
17
- Timeout.timeout(options[:socket_timeout], ClientTimeout) do
18
- sock = new(host, port)
19
- if Socket.constants.include?('TCP_NODELAY') || Socket.constants.include?(:TCP_NODELAY)
20
- sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
21
- end
22
- sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if options.fetch(:keepalive, true)
23
- sock.options = {:host => host, :port => port}.merge(options)
24
- sock
25
- end
26
- end
27
-
28
- # Reads given number of bytes with an optional timeout
29
- #
30
- # @param [Integer] count How many bytes to read
31
- # @param [Integer] timeout Timeout
32
- #
33
- # @return [String] Data read from the socket
34
- # @api public
35
- def read_fully(count, timeout = nil)
36
- return nil if @__bunny_socket_eof_flag__
37
-
38
- value = ''
39
- begin
40
- loop do
41
- value << read_nonblock(count - value.bytesize)
42
- break if value.bytesize >= count
43
- end
44
- rescue EOFError
45
- # @eof will break Rubinius' TCPSocket implementation. MK.
46
- @__bunny_socket_eof_flag__ = true
47
- rescue *READ_RETRY_EXCEPTION_CLASSES
48
- if IO.select([self], nil, nil, timeout)
49
- retry
50
- else
51
- raise Timeout::Error, "IO timeout when reading #{count} bytes"
52
- end
53
- end
54
- value
55
- end # read_fully
56
-
57
- # Writes provided data using IO#write_nonblock, taking care of handling
58
- # of exceptions it raises when writing would fail (e.g. due to socket buffer
59
- # being full).
60
- #
61
- # IMPORTANT: this method will mutate (slice) the argument. Pass in duplicates
62
- # if this is not appropriate in your case.
63
- #
64
- # @param [String] data Data to write
65
- #
66
- # @api public
67
- def write_nonblock_fully(data)
68
- return nil if @__bunny_socket_eof_flag__
69
-
70
- begin
71
- while !data.empty?
72
- written = self.write_nonblock(data)
73
- data.slice!(0, written)
74
- end
75
- rescue Errno::EWOULDBLOCK, Errno::EAGAIN
76
- IO.select([], [self])
77
- retry
78
- end
5
+ module Bunny
6
+ SocketImpl = Socket #JRuby::Socket
7
+ end
8
+ else
9
+ require "bunny/cruby/socket"
79
10
 
80
- data.bytesize
81
- end
11
+ module Bunny
12
+ SocketImpl = Socket
82
13
  end
83
14
  end
@@ -1,57 +1,14 @@
1
- require "socket"
1
+ # See #165. MK.
2
+ if defined?(JRUBY_VERSION)
3
+ require "bunny/jruby/ssl_socket"
2
4
 
3
- module Bunny
4
- begin
5
- require "openssl"
6
-
7
- # TLS-enabled TCP socket that implements convenience
8
- # methods found in Bunny::Socket.
9
- class SSLSocket < OpenSSL::SSL::SSLSocket
10
-
11
- # IO::WaitReadable is 1.9+ only
12
- READ_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK]
13
- READ_RETRY_EXCEPTION_CLASSES << IO::WaitReadable if IO.const_defined?(:WaitReadable)
14
-
15
-
16
- # Reads given number of bytes with an optional timeout
17
- #
18
- # @param [Integer] count How many bytes to read
19
- # @param [Integer] timeout Timeout
20
- #
21
- # @return [String] Data read from the socket
22
- # @api public
23
- def read_fully(count, timeout = nil)
24
- return nil if @__bunny_socket_eof_flag__
5
+ module Bunny
6
+ SSLSocketImpl = JRuby::SSLSocket
7
+ end
8
+ else
9
+ require "bunny/cruby/ssl_socket"
25
10
 
26
- value = ''
27
- begin
28
- loop do
29
- value << read_nonblock(count - value.bytesize)
30
- break if value.bytesize >= count
31
- end
32
- rescue EOFError => e
33
- @__bunny_socket_eof_flag__ = true
34
- rescue OpenSSL::SSL::SSLError => e
35
- if e.message == "read would block"
36
- if IO.select([self], nil, nil, timeout)
37
- retry
38
- else
39
- raise Timeout::Error, "IO timeout when reading #{count} bytes"
40
- end
41
- else
42
- raise e
43
- end
44
- rescue *READ_RETRY_EXCEPTION_CLASSES => e
45
- if IO.select([self], nil, nil, timeout)
46
- retry
47
- else
48
- raise Timeout::Error, "IO timeout when reading #{count} bytes"
49
- end
50
- end
51
- value
52
- end
53
- end
54
- rescue LoadError => le
55
- puts "Could not load OpenSSL"
11
+ module Bunny
12
+ SSLSocketImpl = SSLSocket
56
13
  end
57
14
  end
@@ -219,7 +219,7 @@ module Bunny
219
219
 
220
220
  def self.reacheable?(host, port, timeout)
221
221
  begin
222
- s = Bunny::Socket.open(host, port,
222
+ s = Bunny::SocketImpl.open(host, port,
223
223
  :socket_timeout => timeout)
224
224
 
225
225
  true
@@ -237,7 +237,7 @@ module Bunny
237
237
  def initialize_socket
238
238
  begin
239
239
  @socket = Bunny::Timeout.timeout(@connect_timeout, ClientTimeout) do
240
- Bunny::Socket.open(@host, @port,
240
+ Bunny::SocketImpl.open(@host, @port,
241
241
  :keepalive => @opts[:keepalive],
242
242
  :socket_timeout => @connect_timeout)
243
243
  end
@@ -309,14 +309,7 @@ module Bunny
309
309
  @tls_key = tls_key_from(opts)
310
310
  @tls_certificate_store = opts[:tls_certificate_store]
311
311
 
312
- default_ca_file = ENV[OpenSSL::X509::DEFAULT_CERT_FILE_ENV] || OpenSSL::X509::DEFAULT_CERT_FILE
313
- default_ca_path = ENV[OpenSSL::X509::DEFAULT_CERT_DIR_ENV] || OpenSSL::X509::DEFAULT_CERT_DIR
314
- @tls_ca_certificates = opts.fetch(:tls_ca_certificates, [
315
- default_ca_file,
316
- File.join(default_ca_path, 'ca-certificates.crt'), # Ubuntu/Debian
317
- File.join(default_ca_path, 'ca-bundle.crt'), # Amazon Linux & Fedora/RHEL
318
- File.join(default_ca_path, 'ca-bundle.pem') # OpenSUSE
319
- ])
312
+ @tls_ca_certificates = opts.fetch(:tls_ca_certificates, default_tls_certificates)
320
313
  @verify_peer = opts[:verify_ssl] || opts[:verify_peer]
321
314
 
322
315
  @tls_context = initialize_tls_context(OpenSSL::SSL::SSLContext.new)
@@ -326,7 +319,7 @@ module Bunny
326
319
  raise ArgumentError, "cannot wrap nil into TLS socket, @tls_context is nil. This is a Bunny bug." unless socket
327
320
  raise "cannot wrap a socket into TLS socket, @tls_context is nil. This is a Bunny bug." unless @tls_context
328
321
 
329
- s = Bunny::SSLSocket.new(socket, @tls_context)
322
+ s = Bunny::SSLSocketImpl.new(socket, @tls_context)
330
323
  s.sync_close = true
331
324
  s
332
325
  end
@@ -380,12 +373,30 @@ module Bunny
380
373
  ctx
381
374
  end
382
375
 
376
+ def default_tls_certificates
377
+ if defined?(JRUBY_VERSION)
378
+ # see https://github.com/jruby/jruby/issues/1055. MK.
379
+ []
380
+ else
381
+ default_ca_file = ENV[OpenSSL::X509::DEFAULT_CERT_FILE_ENV] || OpenSSL::X509::DEFAULT_CERT_FILE
382
+ default_ca_path = ENV[OpenSSL::X509::DEFAULT_CERT_DIR_ENV] || OpenSSL::X509::DEFAULT_CERT_DIR
383
+
384
+ [
385
+ default_ca_file,
386
+ File.join(default_ca_path, 'ca-certificates.crt'), # Ubuntu/Debian
387
+ File.join(default_ca_path, 'ca-bundle.crt'), # Amazon Linux & Fedora/RHEL
388
+ File.join(default_ca_path, 'ca-bundle.pem') # OpenSUSE
389
+ ].uniq
390
+ end
391
+ end
392
+
383
393
  def initialize_tls_certificate_store(certs)
384
394
  certs = certs.select { |path| File.readable? path }
385
395
  @logger.debug "Using CA certificates at #{certs.join(', ')}"
386
396
  if certs.empty?
387
397
  @logger.error "No CA certificates found, add one with :tls_ca_certificates"
388
398
  end
399
+ puts certs.inspect
389
400
  OpenSSL::X509::Store.new.tap do |store|
390
401
  certs.each { |path| store.add_file(path) }
391
402
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Bunny
4
4
  # @return [String] Version of the library
5
- VERSION = "1.0.0"
5
+ VERSION = "1.0.1"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bunny
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Duncan
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2013-10-29 00:00:00.000000000 Z
15
+ date: 2013-11-06 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: amq-protocol
@@ -110,11 +110,15 @@ files:
110
110
  - lib/bunny/consumer.rb
111
111
  - lib/bunny/consumer_tag_generator.rb
112
112
  - lib/bunny/consumer_work_pool.rb
113
+ - lib/bunny/cruby/socket.rb
114
+ - lib/bunny/cruby/ssl_socket.rb
113
115
  - lib/bunny/delivery_info.rb
114
116
  - lib/bunny/exceptions.rb
115
117
  - lib/bunny/exchange.rb
116
118
  - lib/bunny/framing.rb
117
119
  - lib/bunny/heartbeat_sender.rb
120
+ - lib/bunny/jruby/socket.rb
121
+ - lib/bunny/jruby/ssl_socket.rb
118
122
  - lib/bunny/message_properties.rb
119
123
  - lib/bunny/queue.rb
120
124
  - lib/bunny/reader_loop.rb