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