amq-client 0.7.0.alpha34 → 0.7.0.alpha35
Sign up to get free protection for your applications and to get access to all the features.
- 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
|