bunny 0.8.0 → 0.9.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.
- data/.gitignore +7 -1
- data/.travis.yml +14 -4
- data/ChangeLog.md +72 -0
- data/Gemfile +17 -11
- data/README.md +82 -0
- data/bunny.gemspec +6 -13
- data/examples/connection/heartbeat.rb +17 -0
- data/lib/bunny.rb +40 -56
- data/lib/bunny/channel.rb +615 -19
- data/lib/bunny/channel_id_allocator.rb +59 -0
- data/lib/bunny/compatibility.rb +24 -0
- data/lib/bunny/concurrent/condition.rb +63 -0
- data/lib/bunny/consumer.rb +42 -26
- data/lib/bunny/consumer_tag_generator.rb +22 -0
- data/lib/bunny/consumer_work_pool.rb +67 -0
- data/lib/bunny/exceptions.rb +128 -0
- data/lib/bunny/exchange.rb +131 -136
- data/lib/bunny/framing.rb +53 -0
- data/lib/bunny/heartbeat_sender.rb +59 -0
- data/lib/bunny/main_loop.rb +70 -0
- data/lib/bunny/message_metadata.rb +126 -0
- data/lib/bunny/queue.rb +102 -275
- data/lib/bunny/session.rb +478 -0
- data/lib/bunny/socket.rb +44 -0
- data/lib/bunny/system_timer.rb +9 -9
- data/lib/bunny/transport.rb +179 -0
- data/lib/bunny/version.rb +1 -1
- data/spec/compatibility/queue_declare_spec.rb +40 -0
- data/spec/higher_level_api/integration/basic_ack_spec.rb +54 -0
- data/spec/higher_level_api/integration/basic_consume_spec.rb +51 -0
- data/spec/higher_level_api/integration/basic_get_spec.rb +47 -0
- data/spec/higher_level_api/integration/basic_nack_spec.rb +39 -0
- data/spec/higher_level_api/integration/basic_publish_spec.rb +105 -0
- data/spec/higher_level_api/integration/basic_qos_spec.rb +32 -0
- data/spec/higher_level_api/integration/basic_recover_spec.rb +18 -0
- data/spec/higher_level_api/integration/basic_reject_spec.rb +53 -0
- data/spec/higher_level_api/integration/basic_return_spec.rb +33 -0
- data/spec/higher_level_api/integration/channel_close_spec.rb +29 -0
- data/spec/higher_level_api/integration/channel_flow_spec.rb +24 -0
- data/spec/higher_level_api/integration/channel_open_spec.rb +57 -0
- data/spec/higher_level_api/integration/channel_open_stress_spec.rb +22 -0
- data/spec/higher_level_api/integration/confirm_select_spec.rb +19 -0
- data/spec/higher_level_api/integration/connection_spec.rb +340 -0
- data/spec/higher_level_api/integration/exchange_bind_spec.rb +31 -0
- data/spec/higher_level_api/integration/exchange_declare_spec.rb +183 -0
- data/spec/higher_level_api/integration/exchange_delete_spec.rb +37 -0
- data/spec/higher_level_api/integration/exchange_unbind_spec.rb +40 -0
- data/spec/higher_level_api/integration/queue_bind_spec.rb +109 -0
- data/spec/higher_level_api/integration/queue_declare_spec.rb +129 -0
- data/spec/higher_level_api/integration/queue_delete_spec.rb +38 -0
- data/spec/higher_level_api/integration/queue_purge_spec.rb +30 -0
- data/spec/higher_level_api/integration/queue_unbind_spec.rb +33 -0
- data/spec/higher_level_api/integration/tx_commit_spec.rb +21 -0
- data/spec/higher_level_api/integration/tx_rollback_spec.rb +21 -0
- data/spec/lower_level_api/integration/basic_cancel_spec.rb +57 -0
- data/spec/lower_level_api/integration/basic_consume_spec.rb +100 -0
- data/spec/spec_helper.rb +64 -0
- data/spec/unit/bunny_spec.rb +15 -0
- data/spec/unit/concurrent/condition_spec.rb +66 -0
- metadata +135 -93
- data/CHANGELOG +0 -21
- data/README.textile +0 -76
- data/Rakefile +0 -14
- data/examples/simple.rb +0 -32
- data/examples/simple_ack.rb +0 -35
- data/examples/simple_consumer.rb +0 -55
- data/examples/simple_fanout.rb +0 -41
- data/examples/simple_headers.rb +0 -42
- data/examples/simple_publisher.rb +0 -29
- data/examples/simple_topic.rb +0 -61
- data/ext/amqp-0.9.1.json +0 -389
- data/ext/config.yml +0 -4
- data/ext/qparser.rb +0 -426
- data/lib/bunny/client.rb +0 -370
- data/lib/bunny/subscription.rb +0 -92
- data/lib/qrack/amq-client-url.rb +0 -165
- data/lib/qrack/channel.rb +0 -20
- data/lib/qrack/client.rb +0 -247
- data/lib/qrack/errors.rb +0 -5
- data/lib/qrack/protocol/protocol.rb +0 -135
- data/lib/qrack/protocol/spec.rb +0 -525
- data/lib/qrack/qrack.rb +0 -20
- data/lib/qrack/queue.rb +0 -40
- data/lib/qrack/subscription.rb +0 -152
- data/lib/qrack/transport/buffer.rb +0 -305
- data/lib/qrack/transport/frame.rb +0 -102
- data/spec/spec_09/amqp_url_spec.rb +0 -19
- data/spec/spec_09/bunny_spec.rb +0 -76
- data/spec/spec_09/connection_spec.rb +0 -34
- data/spec/spec_09/exchange_spec.rb +0 -173
- data/spec/spec_09/queue_spec.rb +0 -240
data/lib/bunny/exchange.rb
CHANGED
@@ -1,166 +1,161 @@
|
|
1
|
-
|
1
|
+
require "bunny/compatibility"
|
2
2
|
|
3
3
|
module Bunny
|
4
|
-
|
5
|
-
# *Exchanges* are the routing and distribution hub of AMQP. All messages that Bunny sends
|
6
|
-
# to an AMQP broker/server @have_to pass through an exchange in order to be routed to a
|
7
|
-
# destination queue. The AMQP specification defines the types of exchange that you can create.
|
8
|
-
#
|
9
|
-
# At the time of writing there are four (4) types of exchange defined:
|
10
|
-
#
|
11
|
-
# * @:direct@
|
12
|
-
# * @:fanout@
|
13
|
-
# * @:topic@
|
14
|
-
# * @:headers@
|
15
|
-
#
|
16
|
-
# AMQP-compliant brokers/servers are required to provide default exchanges for the @direct@ and
|
17
|
-
# @fanout@ exchange types. All default exchanges are prefixed with @'amq.'@, for example:
|
18
|
-
#
|
19
|
-
# * @amq.direct@
|
20
|
-
# * @amq.fanout@
|
21
|
-
# * @amq.topic@
|
22
|
-
# * @amq.match@ or @amq.headers@
|
23
|
-
#
|
24
|
-
# If you want more information about exchanges, please consult the documentation for your
|
25
|
-
# target broker/server or visit the "AMQP website":http://www.amqp.org to find the version of the
|
26
|
-
# specification that applies to your target broker/server.
|
27
4
|
class Exchange
|
28
5
|
|
29
|
-
|
30
|
-
|
31
|
-
def initialize(client, name, opts = {})
|
32
|
-
# check connection to server
|
33
|
-
raise Bunny::ConnectionError, 'Not connected to server' if client.status == :not_connected
|
6
|
+
include Bunny::Compatibility
|
34
7
|
|
35
|
-
@client, @name, @opts = client, name, opts
|
36
8
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
new_type = $1
|
41
|
-
# handle 'amq.match' default
|
42
|
-
new_type = 'headers' if new_type == 'match'
|
43
|
-
@type = new_type.to_sym
|
44
|
-
else
|
45
|
-
@type = opts[:type] || :direct
|
46
|
-
end
|
9
|
+
#
|
10
|
+
# API
|
11
|
+
#
|
47
12
|
|
48
|
-
|
49
|
-
|
13
|
+
# @return [Bunny::Channel]
|
14
|
+
attr_reader :channel
|
50
15
|
|
51
|
-
|
52
|
-
|
53
|
-
opts.delete(:nowait)
|
16
|
+
# @return [String]
|
17
|
+
attr_reader :name
|
54
18
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
:deprecated_ticket => 0, :deprecated_auto_delete => false, :deprecated_internal => false
|
59
|
-
}.merge(opts)
|
19
|
+
# Type of this exchange (one of: :direct, :fanout, :topic, :headers).
|
20
|
+
# @return [Symbol]
|
21
|
+
attr_reader :type
|
60
22
|
|
61
|
-
|
23
|
+
# @return [Symbol]
|
24
|
+
# @api plugin
|
25
|
+
attr_reader :status
|
62
26
|
|
63
|
-
|
27
|
+
# Options hash this exchange instance was instantiated with
|
28
|
+
# @return [Hash]
|
29
|
+
attr_accessor :opts
|
64
30
|
|
65
|
-
client.check_response(method, Qrack::Protocol::Exchange::DeclareOk, "Error declaring exchange #{name}: type = #{type}")
|
66
|
-
end
|
67
|
-
end
|
68
31
|
|
69
|
-
#
|
70
|
-
#
|
32
|
+
# The default exchange. Default exchange is a direct exchange that is predefined.
|
33
|
+
# It cannot be removed. Every queue is bind to this (direct) exchange by default with
|
34
|
+
# the following routing semantics: messages will be routed to the queue withe same
|
35
|
+
# same name as message's routing key. In other words, if a message is published with
|
36
|
+
# a routing key of "weather.usa.ca.sandiego" and there is a queue Q with this name,
|
37
|
+
# that message will be routed to Q.
|
71
38
|
#
|
72
|
-
# @
|
73
|
-
# If set to @true@, the server will only delete the exchange if it has no queue bindings. If the exchange has queue bindings the server does not delete it but raises a channel exception instead.
|
39
|
+
# @param [Bunny::Channel] channel Channel to use.
|
74
40
|
#
|
75
|
-
# @
|
76
|
-
#
|
41
|
+
# @example Publishing a messages to the tasks queue
|
42
|
+
# channel = Bunny::Channel.new(connection)
|
43
|
+
# tasks_queue = channel.queue("tasks")
|
44
|
+
# Bunny::Exchange.default(channel).publish("make clean", routing_key => "tasks")
|
77
45
|
#
|
78
|
-
# @
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
46
|
+
# @see Exchange
|
47
|
+
# @see http://files.travis-ci.org/docs/amqp/0.9.1/AMQP091Specification.pdf AMQP 0.9.1 specification (Section 2.1.2.4)
|
48
|
+
# @note Do not confuse default exchange with amq.direct: amq.direct is a pre-defined direct
|
49
|
+
# exchange that doesn't have any special routing semantics.
|
50
|
+
# @return [Exchange] An instance that corresponds to the default exchange (of type direct).
|
51
|
+
# @api public
|
52
|
+
def self.default(channel_or_connection)
|
53
|
+
self.new(channel_from(channel_or_connection), :direct, AMQ::Protocol::EMPTY_STRING, :no_declare => true)
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def initialize(channel_or_connection, type, name, opts = {})
|
58
|
+
# old Bunny versions pass a connection here. In that case,
|
59
|
+
# we just use default channel from it. MK.
|
60
|
+
@channel = channel_from(channel_or_connection)
|
61
|
+
@name = name
|
62
|
+
@type = type
|
63
|
+
@options = self.class.add_default_options(name, opts)
|
83
64
|
|
84
|
-
|
65
|
+
@durable = @options[:durable]
|
66
|
+
@auto_delete = @options[:auto_delete]
|
67
|
+
@arguments = @options[:arguments]
|
85
68
|
|
86
|
-
|
69
|
+
declare! unless opts[:no_declare] || (@name =~ /^amq\..+/) || (@name == AMQ::Protocol::EMPTY_STRING)
|
87
70
|
|
88
|
-
|
71
|
+
@channel.register_exchange(self)
|
72
|
+
end
|
89
73
|
|
90
|
-
|
74
|
+
# @return [Boolean] true if this exchange was declared as durable (will survive broker restart).
|
75
|
+
# @api public
|
76
|
+
def durable?
|
77
|
+
@durable
|
78
|
+
end # durable?
|
91
79
|
|
92
|
-
|
80
|
+
# @return [Boolean] true if this exchange was declared as automatically deleted (deleted as soon as last consumer unbinds).
|
81
|
+
# @api public
|
82
|
+
def auto_delete?
|
83
|
+
@auto_delete
|
84
|
+
end # auto_delete?
|
93
85
|
|
94
|
-
|
95
|
-
|
86
|
+
def arguments
|
87
|
+
@arguments
|
96
88
|
end
|
97
89
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
#
|
108
|
-
#
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
90
|
+
|
91
|
+
|
92
|
+
def publish(payload, opts = {})
|
93
|
+
@channel.basic_publish(payload, self.name, opts.delete(:routing_key), opts)
|
94
|
+
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
# Deletes the exchange
|
100
|
+
# @api public
|
101
|
+
def delete(opts = {})
|
102
|
+
@channel.exchange_delete(@name, opts)
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def bind(source, opts = {})
|
107
|
+
@channel.exchange_bind(source, self, opts)
|
108
|
+
end
|
109
|
+
|
110
|
+
def unbind(source, opts = {})
|
111
|
+
@channel.exchange_unbind(source, self, opts)
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
def on_return(&block)
|
116
|
+
@on_return = block
|
117
|
+
end
|
118
|
+
|
119
|
+
|
119
120
|
#
|
120
|
-
#
|
121
|
-
# Tells the server whether to persist the message. If set to @true@, the message will
|
122
|
-
# be persisted to disk and not lost if the server restarts. If set to @false@, the message
|
123
|
-
# will not be persisted across server restart. Setting to @true@ incurs a performance penalty
|
124
|
-
# as there is an extra cost associated with disk access.
|
121
|
+
# Implementation
|
125
122
|
#
|
126
|
-
|
127
|
-
def
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
routing_key = opts.delete(:key) || key
|
134
|
-
mandatory = opts.delete(:mandatory)
|
135
|
-
immediate = opts.delete(:immediate)
|
136
|
-
delivery_mode = opts.delete(:persistent) ? 2 : 1
|
137
|
-
content_type = opts.delete(:content_type) || 'application/octet-stream'
|
138
|
-
reply_to = opts.delete(:reply_to)
|
139
|
-
correlation_id = opts.delete(:correlation_id)
|
140
|
-
user_id = opts.delete(:user_id)
|
141
|
-
|
142
|
-
out << Qrack::Protocol::Basic::Publish.new({ :exchange => name,
|
143
|
-
:routing_key => routing_key,
|
144
|
-
:mandatory => mandatory,
|
145
|
-
:immediate => immediate,
|
146
|
-
:deprecated_ticket => 0 })
|
147
|
-
data = data.to_s
|
148
|
-
out << Qrack::Protocol::Header.new(
|
149
|
-
Qrack::Protocol::Basic,
|
150
|
-
data.bytesize, {
|
151
|
-
:content_type => content_type,
|
152
|
-
:delivery_mode => delivery_mode,
|
153
|
-
:reply_to => reply_to,
|
154
|
-
:correlation_id => correlation_id,
|
155
|
-
:user_id => user_id,
|
156
|
-
:priority => 0
|
157
|
-
}.merge(opts)
|
158
|
-
)
|
159
|
-
out << Qrack::Transport::Body.new(data)
|
160
|
-
|
161
|
-
client.send_frame(*out)
|
123
|
+
|
124
|
+
def handle_return(basic_return, properties, content)
|
125
|
+
if @on_return
|
126
|
+
@on_return.call(basic_return, properties, content)
|
127
|
+
else
|
128
|
+
# TODO: log a warning
|
129
|
+
end
|
162
130
|
end
|
163
131
|
|
164
|
-
|
132
|
+
protected
|
165
133
|
|
134
|
+
# @private
|
135
|
+
def declare!
|
136
|
+
@channel.exchange_declare(@name, @type, @options)
|
137
|
+
end
|
138
|
+
|
139
|
+
# @private
|
140
|
+
def self.add_default_options(name, opts, block)
|
141
|
+
{ :exchange => name, :nowait => (block.nil? && !name.empty?) }.merge(opts)
|
142
|
+
end
|
143
|
+
|
144
|
+
# @private
|
145
|
+
def self.add_default_options(name, opts)
|
146
|
+
# :nowait is always false for Bunny
|
147
|
+
h = { :queue => name, :nowait => false }.merge(opts)
|
148
|
+
|
149
|
+
if name.empty?
|
150
|
+
{
|
151
|
+
:passive => false,
|
152
|
+
:durable => false,
|
153
|
+
:auto_delete => false,
|
154
|
+
:arguments => nil
|
155
|
+
}.merge(h)
|
156
|
+
else
|
157
|
+
h
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
166
161
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Bunny
|
2
|
+
module Framing
|
3
|
+
ENCODINGS_SUPPORTED = defined? Encoding
|
4
|
+
HEADER_SLICE = (0..6).freeze
|
5
|
+
DATA_SLICE = (7..-1).freeze
|
6
|
+
PAYLOAD_SLICE = (0..-2).freeze
|
7
|
+
|
8
|
+
module String
|
9
|
+
class Frame < AMQ::Protocol::Frame
|
10
|
+
def self.decode(string)
|
11
|
+
header = string[HEADER_SLICE]
|
12
|
+
type, channel, size = self.decode_header(header)
|
13
|
+
data = string[DATA_SLICE]
|
14
|
+
payload = data[PAYLOAD_SLICE]
|
15
|
+
frame_end = data[-1, 1]
|
16
|
+
|
17
|
+
frame_end.force_encoding(AMQ::Protocol::Frame::FINAL_OCTET.encoding) if ENCODINGS_SUPPORTED
|
18
|
+
|
19
|
+
# 1) the size is miscalculated
|
20
|
+
if payload.bytesize != size
|
21
|
+
raise BadLengthError.new(size, payload.bytesize)
|
22
|
+
end
|
23
|
+
|
24
|
+
# 2) the size is OK, but the string doesn't end with FINAL_OCTET
|
25
|
+
raise NoFinalOctetError.new if frame_end != AMQ::Protocol::Frame::FINAL_OCTET
|
26
|
+
|
27
|
+
self.new(type, payload, channel)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end # String
|
31
|
+
|
32
|
+
|
33
|
+
module IO
|
34
|
+
class Frame < AMQ::Protocol::Frame
|
35
|
+
def self.decode(io)
|
36
|
+
header = io.read(7)
|
37
|
+
type, channel, size = self.decode_header(header)
|
38
|
+
data = io.read_fully(size + 1)
|
39
|
+
payload, frame_end = data[PAYLOAD_SLICE], data[-1, 1]
|
40
|
+
|
41
|
+
# 1) the size is miscalculated
|
42
|
+
if payload.bytesize != size
|
43
|
+
raise BadLengthError.new(size, payload.bytesize)
|
44
|
+
end
|
45
|
+
|
46
|
+
# 2) the size is OK, but the string doesn't end with FINAL_OCTET
|
47
|
+
raise NoFinalOctetError.new if frame_end != AMQ::Protocol::Frame::FINAL_OCTET
|
48
|
+
self.new(type, payload, channel)
|
49
|
+
end # self.from
|
50
|
+
end # Frame
|
51
|
+
end # IO
|
52
|
+
end # Framing
|
53
|
+
end # Bunny
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "thread"
|
2
|
+
require "amq/protocol/client"
|
3
|
+
require "amq/protocol/frame"
|
4
|
+
|
5
|
+
module Bunny
|
6
|
+
class HeartbeatSender
|
7
|
+
|
8
|
+
#
|
9
|
+
# API
|
10
|
+
#
|
11
|
+
|
12
|
+
def initialize(transport)
|
13
|
+
@transport = transport
|
14
|
+
@mutex = Mutex.new
|
15
|
+
|
16
|
+
@last_activity_time = Time.now
|
17
|
+
end
|
18
|
+
|
19
|
+
def start(period = 30)
|
20
|
+
@mutex.synchronize do
|
21
|
+
@period = period
|
22
|
+
|
23
|
+
@thread = Thread.new(&method(:run))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def stop
|
28
|
+
@mutex.synchronize { @thread.exit }
|
29
|
+
end
|
30
|
+
|
31
|
+
def signal_activity!
|
32
|
+
@last_activity_time = Time.now
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def run
|
38
|
+
begin
|
39
|
+
loop do
|
40
|
+
self.beat
|
41
|
+
|
42
|
+
sleep (@period / 2)
|
43
|
+
end
|
44
|
+
rescue IOError => ioe
|
45
|
+
# ignored
|
46
|
+
rescue Exception => e
|
47
|
+
puts e.message
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def beat
|
52
|
+
now = Time.now
|
53
|
+
|
54
|
+
if now > (@last_activity_time + @period)
|
55
|
+
@transport.send_raw(AMQ::Protocol::HeartbeatFrame.encode)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require "thread"
|
2
|
+
|
3
|
+
module Bunny
|
4
|
+
# Network activity loop that reads and passes incoming AMQP 0.9.1 methods for
|
5
|
+
# processing. They are dispatched further down the line in Bunny::Session and Bunny::Channel.
|
6
|
+
# This loop uses a separate thread internally.
|
7
|
+
#
|
8
|
+
# This mimics the way RabbitMQ Java is designed quite closely.
|
9
|
+
class MainLoop
|
10
|
+
|
11
|
+
def initialize(transport, session)
|
12
|
+
@transport = transport
|
13
|
+
@session = session
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def start
|
18
|
+
@thread = Thread.new(&method(:run_loop))
|
19
|
+
end
|
20
|
+
|
21
|
+
def run_loop
|
22
|
+
loop do
|
23
|
+
begin
|
24
|
+
break if @stopping
|
25
|
+
|
26
|
+
frame = @transport.read_next_frame
|
27
|
+
@session.signal_activity!
|
28
|
+
|
29
|
+
next if frame.is_a?(AMQ::Protocol::HeartbeatFrame)
|
30
|
+
|
31
|
+
if !frame.final? || frame.method_class.has_content?
|
32
|
+
header = @transport.read_next_frame
|
33
|
+
content = ''
|
34
|
+
|
35
|
+
if header.body_size > 0
|
36
|
+
loop do
|
37
|
+
body_frame = @transport.read_next_frame
|
38
|
+
content << body_frame.decode_payload
|
39
|
+
|
40
|
+
break if content.bytesize >= header.body_size
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
@session.handle_frameset(frame.channel, [frame.decode_payload, header.decode_payload, content])
|
45
|
+
else
|
46
|
+
@session.handle_frame(frame.channel, frame.decode_payload)
|
47
|
+
end
|
48
|
+
rescue Timeout::Error => te
|
49
|
+
# given that the server may be pushing data to us, timeout detection/handling
|
50
|
+
# should happen per operation and not in this loop
|
51
|
+
rescue Errno::EBADF => ebadf
|
52
|
+
# ignored, happens when we loop after the transport has already been closed
|
53
|
+
rescue Exception => e
|
54
|
+
puts e.class.name
|
55
|
+
puts e.message
|
56
|
+
puts e.backtrace
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def stop
|
62
|
+
@stopping = true
|
63
|
+
end
|
64
|
+
|
65
|
+
def kill
|
66
|
+
@thread.kill
|
67
|
+
@thread.join
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|