amq-client 0.7.0.alpha34 → 0.7.0.alpha35
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/.travis.yml +4 -0
- data/Gemfile +1 -1
- data/README.textile +1 -1
- data/bin/ci/before_build.sh +24 -0
- data/examples/eventmachine_adapter/extensions/rabbitmq/handling_confirm_select_ok.rb +2 -2
- data/examples/eventmachine_adapter/extensions/rabbitmq/publisher_confirmations_with_transient_messages.rb +1 -1
- data/examples/eventmachine_adapter/extensions/rabbitmq/publisher_confirmations_with_unroutable_message.rb +1 -1
- data/lib/amq/client.rb +29 -17
- data/lib/amq/client/adapter.rb +8 -504
- data/lib/amq/client/adapters/coolio.rb +4 -282
- data/lib/amq/client/adapters/event_machine.rb +4 -382
- data/lib/amq/client/async/adapter.rb +517 -0
- data/lib/amq/client/async/adapters/coolio.rb +291 -0
- data/lib/amq/client/async/adapters/event_machine.rb +392 -0
- data/lib/amq/client/async/adapters/eventmachine.rb +1 -0
- data/lib/amq/client/async/callbacks.rb +71 -0
- data/lib/amq/client/async/channel.rb +385 -0
- data/lib/amq/client/async/entity.rb +66 -0
- data/lib/amq/client/async/exchange.rb +157 -0
- data/lib/amq/client/async/extensions/rabbitmq/basic.rb +38 -0
- data/lib/amq/client/async/extensions/rabbitmq/confirm.rb +248 -0
- data/lib/amq/client/async/queue.rb +455 -0
- data/lib/amq/client/callbacks.rb +6 -65
- data/lib/amq/client/channel.rb +4 -376
- data/lib/amq/client/entity.rb +6 -57
- data/lib/amq/client/exchange.rb +4 -148
- data/lib/amq/client/extensions/rabbitmq/basic.rb +4 -28
- data/lib/amq/client/extensions/rabbitmq/confirm.rb +5 -240
- data/lib/amq/client/queue.rb +5 -450
- data/lib/amq/client/version.rb +1 -1
- data/spec/unit/client_spec.rb +10 -30
- metadata +16 -22
@@ -0,0 +1,291 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# http://coolio.github.com
|
4
|
+
|
5
|
+
require "cool.io"
|
6
|
+
require "amq/client"
|
7
|
+
require "amq/client/framing/string/frame"
|
8
|
+
|
9
|
+
module AMQ
|
10
|
+
module Client
|
11
|
+
module Async
|
12
|
+
#
|
13
|
+
# CoolioClient is a drop-in replacement for EventMachineClient, if you prefer
|
14
|
+
# cool.io style.
|
15
|
+
#
|
16
|
+
class CoolioClient
|
17
|
+
|
18
|
+
#
|
19
|
+
# Behaviors
|
20
|
+
#
|
21
|
+
|
22
|
+
include AMQ::Client::Async::Adapter
|
23
|
+
|
24
|
+
|
25
|
+
#
|
26
|
+
# API
|
27
|
+
#
|
28
|
+
|
29
|
+
|
30
|
+
#
|
31
|
+
# Cool.io socket delegates most of its operations to the parent adapter.
|
32
|
+
# Thus, 99.9% of the time you don't need to deal with this class.
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
# @private
|
36
|
+
class Socket < ::Coolio::TCPSocket
|
37
|
+
attr_accessor :adapter
|
38
|
+
|
39
|
+
# Connects to given host/port and sets parent adapter.
|
40
|
+
#
|
41
|
+
# @param [CoolioClient]
|
42
|
+
# @param [String]
|
43
|
+
# @param [Fixnum]
|
44
|
+
def self.connect(adapter, host, port)
|
45
|
+
socket = super(host, port)
|
46
|
+
socket.adapter = adapter
|
47
|
+
socket
|
48
|
+
end
|
49
|
+
|
50
|
+
# Triggers socket_connect callback
|
51
|
+
def on_connect
|
52
|
+
#puts "On connect"
|
53
|
+
adapter.socket_connected
|
54
|
+
end
|
55
|
+
|
56
|
+
# Triggers on_read callback
|
57
|
+
def on_read(data)
|
58
|
+
# puts "Received data"
|
59
|
+
# puts_data(data)
|
60
|
+
adapter.receive_data(data)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Triggers socket_disconnect callback
|
64
|
+
def on_close
|
65
|
+
adapter.socket_disconnected
|
66
|
+
end
|
67
|
+
|
68
|
+
# Triggers tcp_connection_failed callback
|
69
|
+
def on_connect_failed
|
70
|
+
adapter.tcp_connection_failed
|
71
|
+
end
|
72
|
+
|
73
|
+
# Sends raw data through the socket
|
74
|
+
#
|
75
|
+
# param [String] Binary data
|
76
|
+
def send_raw(data)
|
77
|
+
# puts "Sending data"
|
78
|
+
# puts_data(data)
|
79
|
+
write(data)
|
80
|
+
end
|
81
|
+
|
82
|
+
protected
|
83
|
+
# Debugging routine
|
84
|
+
def puts_data(data)
|
85
|
+
puts " As string: #{data.inspect}"
|
86
|
+
puts " As byte array: #{data.bytes.to_a.inspect}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
# Cool.io socket for multiplexing et al.
|
93
|
+
#
|
94
|
+
# @private
|
95
|
+
attr_accessor :socket
|
96
|
+
|
97
|
+
# Hash with available callbacks
|
98
|
+
attr_accessor :callbacks
|
99
|
+
|
100
|
+
# Creates a socket and attaches it to cool.io default loop.
|
101
|
+
#
|
102
|
+
# Called from CoolioClient.connect
|
103
|
+
#
|
104
|
+
# @see AMQ::Client::Adapter::ClassMethods#connect
|
105
|
+
# @param [Hash] connection settings
|
106
|
+
# @api private
|
107
|
+
def establish_connection(settings)
|
108
|
+
@settings = Settings.configure(settings)
|
109
|
+
|
110
|
+
socket = Socket.connect(self, @settings[:host], @settings[:port])
|
111
|
+
socket.attach(Cool.io::Loop.default)
|
112
|
+
self.socket = socket
|
113
|
+
|
114
|
+
|
115
|
+
@on_tcp_connection_failure = @settings[:on_tcp_connection_failure] || Proc.new { |settings|
|
116
|
+
raise self.class.tcp_connection_failure_exception_class.new(settings)
|
117
|
+
}
|
118
|
+
@on_possible_authentication_failure = @settings[:on_possible_authentication_failure] || Proc.new { |settings|
|
119
|
+
raise self.class.authentication_failure_exception_class.new(settings)
|
120
|
+
}
|
121
|
+
|
122
|
+
@locale = @settings.fetch(:locale, "en_GB")
|
123
|
+
@client_properties = Settings.client_properties.merge(@settings.fetch(:client_properties, Hash.new))
|
124
|
+
|
125
|
+
socket
|
126
|
+
end
|
127
|
+
|
128
|
+
# Registers on_open callback
|
129
|
+
# @see #on_open
|
130
|
+
# @api private
|
131
|
+
def register_connection_callback(&block)
|
132
|
+
self.on_open(&block)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Performs basic initialization. Do not use this method directly, use
|
136
|
+
# CoolioClient.connect instead
|
137
|
+
#
|
138
|
+
# @see AMQ::Client::Adapter::ClassMethods#connect
|
139
|
+
# @api private
|
140
|
+
def initialize
|
141
|
+
# Be careful with default values for #ruby hashes: h = Hash.new(Array.new); h[:key] ||= 1
|
142
|
+
# won't assign anything to :key. MK.
|
143
|
+
@callbacks = Hash.new
|
144
|
+
|
145
|
+
self.logger = self.class.logger
|
146
|
+
|
147
|
+
@frames = Array.new
|
148
|
+
@channels = Hash.new
|
149
|
+
|
150
|
+
@mechanism = "PLAIN"
|
151
|
+
end
|
152
|
+
|
153
|
+
# Sets a callback for successful connection (after we receive open-ok)
|
154
|
+
#
|
155
|
+
# @api public
|
156
|
+
def on_open(&block)
|
157
|
+
define_callback :connect, &block
|
158
|
+
end
|
159
|
+
alias on_connection on_open
|
160
|
+
|
161
|
+
# Sets a callback for disconnection (as in client-side disconnection)
|
162
|
+
#
|
163
|
+
# @api public
|
164
|
+
def on_closed(&block)
|
165
|
+
define_callback :disconnect, &block
|
166
|
+
end
|
167
|
+
alias on_disconnection on_closed
|
168
|
+
|
169
|
+
# Sets a callback for tcp connection failure (as in can't make initial connection)
|
170
|
+
def on_tcp_connection_failure(&block)
|
171
|
+
define_callback :tcp_connection_failure, &block
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
# Called by AMQ::Client::Connection after we receive connection.open-ok.
|
176
|
+
#
|
177
|
+
# @api private
|
178
|
+
def connection_successful
|
179
|
+
@authenticating = false
|
180
|
+
opened!
|
181
|
+
|
182
|
+
exec_callback_yielding_self(:connect)
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
# Called by AMQ::Client::Connection after we receive connection.close-ok.
|
187
|
+
#
|
188
|
+
# @api private
|
189
|
+
def disconnection_successful
|
190
|
+
exec_callback_yielding_self(:disconnect)
|
191
|
+
close_connection
|
192
|
+
closed!
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
|
197
|
+
# Called when socket is connected but before handshake is done
|
198
|
+
#
|
199
|
+
# @api private
|
200
|
+
def socket_connected
|
201
|
+
post_init
|
202
|
+
end
|
203
|
+
|
204
|
+
# Called after socket is closed
|
205
|
+
#
|
206
|
+
# @api private
|
207
|
+
def socket_disconnected
|
208
|
+
end
|
209
|
+
|
210
|
+
alias close disconnect
|
211
|
+
|
212
|
+
|
213
|
+
self.handle(Protocol::Connection::Start) do |connection, frame|
|
214
|
+
connection.handle_start(frame.decode_payload)
|
215
|
+
end
|
216
|
+
|
217
|
+
self.handle(Protocol::Connection::Tune) do |connection, frame|
|
218
|
+
connection.handle_tune(frame.decode_payload)
|
219
|
+
|
220
|
+
connection.open(connection.vhost)
|
221
|
+
end
|
222
|
+
|
223
|
+
self.handle(Protocol::Connection::OpenOk) do |connection, frame|
|
224
|
+
connection.handle_open_ok(frame.decode_payload)
|
225
|
+
end
|
226
|
+
|
227
|
+
self.handle(Protocol::Connection::Close) do |connection, frame|
|
228
|
+
connection.handle_close(frame.decode_payload)
|
229
|
+
end
|
230
|
+
|
231
|
+
self.handle(Protocol::Connection::CloseOk) do |connection, frame|
|
232
|
+
connection.handle_close_ok(frame.decode_payload)
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
|
237
|
+
|
238
|
+
|
239
|
+
# Sends raw data through the socket
|
240
|
+
#
|
241
|
+
# @param [String] binary data
|
242
|
+
# @api private
|
243
|
+
def send_raw(data)
|
244
|
+
socket.send_raw data
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
# The story about the buffering is kinda similar to EventMachine,
|
249
|
+
# you keep receiving more than one frame in a single packet.
|
250
|
+
#
|
251
|
+
# @param [String] chunk with binary data received. It could be one frame,
|
252
|
+
# more than one frame or less than one frame.
|
253
|
+
# @api private
|
254
|
+
def receive_data(chunk)
|
255
|
+
@chunk_buffer << chunk
|
256
|
+
while frame = get_next_frame
|
257
|
+
receive_frame(AMQ::Client::Framing::String::Frame.decode(frame))
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# Closes the socket.
|
262
|
+
#
|
263
|
+
# @api private
|
264
|
+
def close_connection
|
265
|
+
@socket.close
|
266
|
+
end
|
267
|
+
|
268
|
+
# Returns class used for tcp connection failures.
|
269
|
+
#
|
270
|
+
# @api private
|
271
|
+
def self.tcp_connection_failure_exception_class
|
272
|
+
AMQ::Client::TCPConnectionFailed
|
273
|
+
end # self.tcp_connection_failure_exception_class
|
274
|
+
|
275
|
+
protected
|
276
|
+
|
277
|
+
# @api private
|
278
|
+
def post_init
|
279
|
+
self.reset
|
280
|
+
self.handshake
|
281
|
+
end
|
282
|
+
|
283
|
+
# @api private
|
284
|
+
def reset
|
285
|
+
@chunk_buffer = ""
|
286
|
+
@frames = Array.new
|
287
|
+
end
|
288
|
+
end # CoolioClient
|
289
|
+
end # Async
|
290
|
+
end # Client
|
291
|
+
end # AMQ
|
@@ -0,0 +1,392 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "eventmachine"
|
4
|
+
require "amq/client"
|
5
|
+
require "amq/client/async/adapter"
|
6
|
+
require "amq/client/framing/string/frame"
|
7
|
+
|
8
|
+
module AMQ
|
9
|
+
module Client
|
10
|
+
module Async
|
11
|
+
class EventMachineClient < EM::Connection
|
12
|
+
|
13
|
+
#
|
14
|
+
# Behaviors
|
15
|
+
#
|
16
|
+
|
17
|
+
include AMQ::Client::Async::Adapter
|
18
|
+
|
19
|
+
|
20
|
+
#
|
21
|
+
# API
|
22
|
+
#
|
23
|
+
|
24
|
+
# Initiates connection to AMQP broker. If callback is given, runs it when (and if) AMQP connection
|
25
|
+
# succeeds.
|
26
|
+
#
|
27
|
+
# @option settings [String] :host ("127.0.0.1") Hostname AMQ broker runs on.
|
28
|
+
# @option settings [String] :port (5672) Port AMQ broker listens on.
|
29
|
+
# @option settings [String] :vhost ("/") Virtual host to use.
|
30
|
+
# @option settings [String] :user ("guest") Username to use for authentication.
|
31
|
+
# @option settings [String] :pass ("guest") Password to use for authentication.
|
32
|
+
# @option settings [String] :ssl (false) Should be use TLS (SSL) for connection?
|
33
|
+
# @option settings [String] :timeout (nil) Connection timeout.
|
34
|
+
# @option settings [String] :logging (false) Turns logging on or off.
|
35
|
+
# @option settings [String] :broker (nil) Broker name (use if you intend to use broker-specific features).
|
36
|
+
# @option settings [Fixnum] :frame_max (131072) Maximum frame size to use. If broker cannot support frames this large, broker's maximum value will be used instead.
|
37
|
+
#
|
38
|
+
# @param [Hash] settings
|
39
|
+
def self.connect(settings = {}, &block)
|
40
|
+
@settings = Settings.configure(settings)
|
41
|
+
|
42
|
+
instance = EventMachine.connect(@settings[:host], @settings[:port], self, @settings)
|
43
|
+
instance.register_connection_callback(&block)
|
44
|
+
|
45
|
+
instance
|
46
|
+
end
|
47
|
+
|
48
|
+
# Reconnect after a period of wait.
|
49
|
+
#
|
50
|
+
# @param [Fixnum] period Period of time, in seconds, to wait before reconnection attempt.
|
51
|
+
# @param [Boolean] force If true, enforces immediate reconnection.
|
52
|
+
# @api public
|
53
|
+
def reconnect(force = false, period = 5)
|
54
|
+
if @reconnecting and not force
|
55
|
+
EventMachine::Timer.new(period) {
|
56
|
+
reconnect(true, period)
|
57
|
+
}
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
if !@reconnecting
|
62
|
+
@reconnecting = true
|
63
|
+
|
64
|
+
self.handle_connection_interruption
|
65
|
+
self.reset
|
66
|
+
end
|
67
|
+
|
68
|
+
EventMachine.reconnect(@settings[:host], @settings[:port], self)
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
# Defines a callback that will be executed when AMQP connection is considered open:
|
76
|
+
# client and broker has agreed on max channel identifier and maximum allowed frame
|
77
|
+
# size and authentication succeeds. You can define more than one callback.
|
78
|
+
#
|
79
|
+
# @see on_possible_authentication_failure
|
80
|
+
# @api public
|
81
|
+
def on_open(&block)
|
82
|
+
@connection_deferrable.callback(&block)
|
83
|
+
end # on_open(&block)
|
84
|
+
alias on_connection on_open
|
85
|
+
|
86
|
+
# Defines a callback that will be run when broker confirms connection termination
|
87
|
+
# (client receives connection.close-ok). You can define more than one callback.
|
88
|
+
#
|
89
|
+
# @api public
|
90
|
+
def on_closed(&block)
|
91
|
+
@disconnection_deferrable.callback(&block)
|
92
|
+
end # on_closed(&block)
|
93
|
+
alias on_disconnection on_closed
|
94
|
+
|
95
|
+
# Defines a callback that will be run when initial TCP connection fails.
|
96
|
+
# You can define only one callback.
|
97
|
+
#
|
98
|
+
# @api public
|
99
|
+
def on_tcp_connection_failure(&block)
|
100
|
+
@on_tcp_connection_failure = block
|
101
|
+
end
|
102
|
+
|
103
|
+
# Defines a callback that will be run when TCP connection to AMQP broker is lost (interrupted).
|
104
|
+
# You can define only one callback.
|
105
|
+
#
|
106
|
+
# @api public
|
107
|
+
def on_tcp_connection_loss(&block)
|
108
|
+
@on_tcp_connection_loss = block
|
109
|
+
end
|
110
|
+
|
111
|
+
# Defines a callback that will be run when TCP connection is closed before authentication
|
112
|
+
# finishes. Usually this means authentication failure. You can define only one callback.
|
113
|
+
#
|
114
|
+
# @api public
|
115
|
+
def on_possible_authentication_failure(&block)
|
116
|
+
@on_possible_authentication_failure = block
|
117
|
+
end
|
118
|
+
|
119
|
+
# @see #on_open
|
120
|
+
# @private
|
121
|
+
def register_connection_callback(&block)
|
122
|
+
unless block.nil?
|
123
|
+
# delay calling block we were given till after we receive
|
124
|
+
# connection.open-ok. Connection will notify us when
|
125
|
+
# that happens.
|
126
|
+
self.on_open do
|
127
|
+
block.call(self)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
def initialize(*args)
|
136
|
+
super(*args)
|
137
|
+
|
138
|
+
self.logger = self.class.logger
|
139
|
+
|
140
|
+
@frames = Array.new
|
141
|
+
@channels = Hash.new
|
142
|
+
@callbacks = Hash.new
|
143
|
+
|
144
|
+
opening!
|
145
|
+
|
146
|
+
# track TCP connection state, used to detect initial TCP connection failures.
|
147
|
+
@tcp_connection_established = false
|
148
|
+
@tcp_connection_failed = false
|
149
|
+
@intentionally_closing_connection = false
|
150
|
+
|
151
|
+
# EventMachine::Connection's and Adapter's constructors arity
|
152
|
+
# make it easier to use *args. MK.
|
153
|
+
@settings = Settings.configure(args.first)
|
154
|
+
@on_tcp_connection_failure = @settings[:on_tcp_connection_failure] || Proc.new { |settings|
|
155
|
+
raise self.class.tcp_connection_failure_exception_class.new(settings)
|
156
|
+
}
|
157
|
+
@on_possible_authentication_failure = @settings[:on_possible_authentication_failure] || Proc.new { |settings|
|
158
|
+
raise self.class.authentication_failure_exception_class.new(settings)
|
159
|
+
}
|
160
|
+
|
161
|
+
@mechanism = "PLAIN"
|
162
|
+
@locale = @settings.fetch(:locale, "en_GB")
|
163
|
+
@client_properties = Settings.client_properties.merge(@settings.fetch(:client_properties, Hash.new))
|
164
|
+
|
165
|
+
self.reset
|
166
|
+
self.set_pending_connect_timeout((@settings[:timeout] || 3).to_f) unless defined?(JRUBY_VERSION)
|
167
|
+
|
168
|
+
if self.heartbeat_interval > 0
|
169
|
+
@last_server_heartbeat = Time.now
|
170
|
+
EventMachine.add_periodic_timer(self.heartbeat_interval, &method(:send_heartbeat))
|
171
|
+
end
|
172
|
+
end # initialize(*args)
|
173
|
+
|
174
|
+
|
175
|
+
|
176
|
+
# For EventMachine adapter, this is a no-op.
|
177
|
+
# @api public
|
178
|
+
def establish_connection(settings)
|
179
|
+
# Unfortunately there doesn't seem to be any sane way
|
180
|
+
# how to get EventMachine connect to the instance level.
|
181
|
+
end
|
182
|
+
|
183
|
+
alias close disconnect
|
184
|
+
|
185
|
+
|
186
|
+
|
187
|
+
# Whether we are in authentication state (after TCP connection was estabilished
|
188
|
+
# but before broker authenticated us).
|
189
|
+
#
|
190
|
+
# @return [Boolean]
|
191
|
+
# @api public
|
192
|
+
def authenticating?
|
193
|
+
@authenticating
|
194
|
+
end # authenticating?
|
195
|
+
|
196
|
+
# IS TCP connection estabilished and currently active?
|
197
|
+
# @return [Boolean]
|
198
|
+
# @api public
|
199
|
+
def tcp_connection_established?
|
200
|
+
@tcp_connection_established
|
201
|
+
end # tcp_connection_established?
|
202
|
+
|
203
|
+
|
204
|
+
|
205
|
+
|
206
|
+
|
207
|
+
|
208
|
+
#
|
209
|
+
# Implementation
|
210
|
+
#
|
211
|
+
|
212
|
+
# Backwards compatibility with 0.7.0.a25. MK.
|
213
|
+
Deferrable = EventMachine::DefaultDeferrable
|
214
|
+
|
215
|
+
|
216
|
+
alias send_raw send_data
|
217
|
+
|
218
|
+
|
219
|
+
# EventMachine reactor callback. Is run when TCP connection is estabilished
|
220
|
+
# but before resumption of the network loop. Note that this includes cases
|
221
|
+
# when TCP connection has failed.
|
222
|
+
# @private
|
223
|
+
def post_init
|
224
|
+
reset
|
225
|
+
|
226
|
+
# note that upgrading to TLS in #connection_completed causes
|
227
|
+
# Erlang SSL app that RabbitMQ relies on to report
|
228
|
+
# error on TCP connection <0.1465.0>:{ssl_upgrade_error,"record overflow"}
|
229
|
+
# and close TCP connection down. Investigation of this issue is likely
|
230
|
+
# to take some time and to not be worth in as long as #post_init
|
231
|
+
# works fine. MK.
|
232
|
+
upgrade_to_tls_if_necessary
|
233
|
+
rescue Exception => error
|
234
|
+
raise error
|
235
|
+
end # post_init
|
236
|
+
|
237
|
+
|
238
|
+
|
239
|
+
# Called by EventMachine reactor once TCP connection is successfully estabilished.
|
240
|
+
# @private
|
241
|
+
def connection_completed
|
242
|
+
# we only can safely set this value here because EventMachine is a lovely piece of
|
243
|
+
# software that calls #post_init before #unbind even when TCP connection
|
244
|
+
# fails. MK.
|
245
|
+
@tcp_connection_established = true
|
246
|
+
# again, this is because #unbind is called in different situations
|
247
|
+
# and there is no easy way to tell initial connection failure
|
248
|
+
# from connection loss. Not in EventMachine 0.12.x, anyway. MK.
|
249
|
+
@had_successfull_connected_before = true
|
250
|
+
|
251
|
+
@reconnecting = false
|
252
|
+
|
253
|
+
self.handshake
|
254
|
+
end
|
255
|
+
|
256
|
+
# @private
|
257
|
+
def close_connection(*args)
|
258
|
+
@intentionally_closing_connection = true
|
259
|
+
|
260
|
+
super(*args)
|
261
|
+
end
|
262
|
+
|
263
|
+
# Called by EventMachine reactor when
|
264
|
+
#
|
265
|
+
# * We close TCP connection down
|
266
|
+
# * Our peer closes TCP connection down
|
267
|
+
# * There is a network connection issue
|
268
|
+
# * Initial TCP connection fails
|
269
|
+
# @private
|
270
|
+
def unbind(exception = nil)
|
271
|
+
if !@tcp_connection_established && !@had_successfull_connected_before && !@intentionally_closing_connection
|
272
|
+
@tcp_connection_failed = true
|
273
|
+
self.tcp_connection_failed
|
274
|
+
end
|
275
|
+
|
276
|
+
closing!
|
277
|
+
@tcp_connection_established = false
|
278
|
+
|
279
|
+
self.handle_connection_interruption
|
280
|
+
@disconnection_deferrable.succeed
|
281
|
+
|
282
|
+
closed!
|
283
|
+
|
284
|
+
|
285
|
+
self.tcp_connection_lost if !@intentionally_closing_connection && @had_successfull_connected_before
|
286
|
+
|
287
|
+
# since AMQP spec dictates that authentication failure is a protocol exception
|
288
|
+
# and protocol exceptions result in connection closure, check whether we are
|
289
|
+
# in the authentication stage. If so, it is likely to signal an authentication
|
290
|
+
# issue. Java client behaves the same way. MK.
|
291
|
+
if authenticating? && !@intentionally_closing_connection
|
292
|
+
@on_possible_authentication_failure.call(@settings) if @on_possible_authentication_failure
|
293
|
+
end
|
294
|
+
end # unbind
|
295
|
+
|
296
|
+
|
297
|
+
#
|
298
|
+
# EventMachine receives data in chunks, sometimes those chunks are smaller
|
299
|
+
# than the size of AMQP frame. That's why you need to add some kind of buffer.
|
300
|
+
#
|
301
|
+
# @private
|
302
|
+
def receive_data(chunk)
|
303
|
+
@chunk_buffer << chunk
|
304
|
+
while frame = get_next_frame
|
305
|
+
self.receive_frame(AMQ::Client::Framing::String::Frame.decode(frame))
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
|
310
|
+
# Called by AMQ::Client::Connection after we receive connection.open-ok.
|
311
|
+
# @api public
|
312
|
+
def connection_successful
|
313
|
+
@authenticating = false
|
314
|
+
opened!
|
315
|
+
|
316
|
+
@connection_deferrable.succeed
|
317
|
+
end # connection_successful
|
318
|
+
|
319
|
+
|
320
|
+
# Called by AMQ::Client::Connection after we receive connection.close-ok.
|
321
|
+
#
|
322
|
+
# @api public
|
323
|
+
def disconnection_successful
|
324
|
+
@disconnection_deferrable.succeed
|
325
|
+
|
326
|
+
# true for "after writing buffered data"
|
327
|
+
self.close_connection(true)
|
328
|
+
self.reset
|
329
|
+
closed!
|
330
|
+
end # disconnection_successful
|
331
|
+
|
332
|
+
|
333
|
+
|
334
|
+
|
335
|
+
|
336
|
+
self.handle(Protocol::Connection::Start) do |connection, frame|
|
337
|
+
connection.handle_start(frame.decode_payload)
|
338
|
+
end
|
339
|
+
|
340
|
+
self.handle(Protocol::Connection::Tune) do |connection, frame|
|
341
|
+
connection.handle_tune(frame.decode_payload)
|
342
|
+
|
343
|
+
connection.open(connection.vhost)
|
344
|
+
end
|
345
|
+
|
346
|
+
self.handle(Protocol::Connection::OpenOk) do |connection, frame|
|
347
|
+
connection.handle_open_ok(frame.decode_payload)
|
348
|
+
end
|
349
|
+
|
350
|
+
self.handle(Protocol::Connection::Close) do |connection, frame|
|
351
|
+
connection.handle_close(frame.decode_payload)
|
352
|
+
end
|
353
|
+
|
354
|
+
self.handle(Protocol::Connection::CloseOk) do |connection, frame|
|
355
|
+
connection.handle_close_ok(frame.decode_payload)
|
356
|
+
end
|
357
|
+
|
358
|
+
|
359
|
+
|
360
|
+
|
361
|
+
protected
|
362
|
+
|
363
|
+
|
364
|
+
def reset
|
365
|
+
@size = 0
|
366
|
+
@payload = ""
|
367
|
+
@frames = Array.new
|
368
|
+
|
369
|
+
@chunk_buffer = ""
|
370
|
+
@connection_deferrable = EventMachine::DefaultDeferrable.new
|
371
|
+
@disconnection_deferrable = EventMachine::DefaultDeferrable.new
|
372
|
+
|
373
|
+
# used to track down whether authentication succeeded. AMQP 0.9.1 dictates
|
374
|
+
# that on authentication failure broker must close TCP connection without sending
|
375
|
+
# any more data. This is why we need to explicitly track whether we are past
|
376
|
+
# authentication stage to signal possible authentication failures.
|
377
|
+
@authenticating = false
|
378
|
+
end
|
379
|
+
|
380
|
+
def upgrade_to_tls_if_necessary
|
381
|
+
tls_options = @settings[:ssl]
|
382
|
+
|
383
|
+
if tls_options.is_a?(Hash)
|
384
|
+
start_tls(tls_options)
|
385
|
+
elsif tls_options
|
386
|
+
start_tls
|
387
|
+
end
|
388
|
+
end # upgrade_to_tls_if_necessary
|
389
|
+
end # EventMachineClient
|
390
|
+
end # Async
|
391
|
+
end # Client
|
392
|
+
end # AMQ
|