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.
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