amq-client 0.7.0.alpha25 → 0.7.0.alpha26

Sign up to get free protection for your applications and to get access to all the features.
@@ -13,6 +13,19 @@ module AMQ
13
13
  # cool.io style.
14
14
  #
15
15
  class CoolioClient
16
+
17
+ #
18
+ # Behaviors
19
+ #
20
+
21
+ include AMQ::Client::Adapter
22
+
23
+
24
+ #
25
+ # API
26
+ #
27
+
28
+
16
29
  #
17
30
  # Cool.io socket delegates most of its operations to the parent adapter.
18
31
  # Thus, 99.9% of the time you don't need to deal with this class.
@@ -73,17 +86,7 @@ module AMQ
73
86
  end
74
87
  end
75
88
 
76
- #
77
- # Behaviors
78
- #
79
89
 
80
- include AMQ::Client::Adapter
81
- include AMQ::Client::Callbacks
82
-
83
-
84
- #
85
- # API
86
- #
87
90
 
88
91
  # Cool.io socket for multiplexing et al.
89
92
  #
@@ -93,11 +96,6 @@ module AMQ
93
96
  # Hash with available callbacks
94
97
  attr_accessor :callbacks
95
98
 
96
- # AMQP connections
97
- #
98
- # @see AMQ::Client::Connection
99
- attr_accessor :connections
100
-
101
99
  # Creates a socket and attaches it to cool.io default loop.
102
100
  #
103
101
  # Called from CoolioClient.connect
@@ -106,9 +104,24 @@ module AMQ
106
104
  # @param [Hash] connection settings
107
105
  # @api private
108
106
  def establish_connection(settings)
109
- socket = Socket.connect(self, settings[:host], settings[:port])
107
+ @settings = Settings.configure(settings)
108
+
109
+ socket = Socket.connect(self, @settings[:host], @settings[:port])
110
110
  socket.attach(Cool.io::Loop.default)
111
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
112
125
  end
113
126
 
114
127
  # Registers on_open callback
@@ -127,11 +140,13 @@ module AMQ
127
140
  # Be careful with default values for #ruby hashes: h = Hash.new(Array.new); h[:key] ||= 1
128
141
  # won't assign anything to :key. MK.
129
142
  @callbacks = {}
130
- @connections = []
131
- super
132
- if settings[:on_tcp_connection_failure]
133
- on_tcp_connection_failure(&settings.delete(:on_tcp_connection_failure))
134
- end
143
+
144
+ self.logger = self.class.logger
145
+
146
+ @frames = Array.new
147
+ @channels = Hash.new
148
+
149
+ @mechanism = "PLAIN"
135
150
  end
136
151
 
137
152
  # Sets a callback for successful connection (after we receive open-ok)
@@ -177,16 +192,6 @@ module AMQ
177
192
  end
178
193
 
179
194
 
180
- # Called by Socket if it could not connect.
181
- #
182
- # @api private
183
- def tcp_connection_failed
184
- if has_callback?(:tcp_connection_failure)
185
- exec_callback_yielding_self(:tcp_connection_failure)
186
- else
187
- raise self.class.tcp_connection_failure_exception_class.new(settings)
188
- end
189
- end # tcp_connection_failed
190
195
 
191
196
  # Called when socket is connected but before handshake is done
192
197
  #
@@ -201,6 +206,35 @@ module AMQ
201
206
  def socket_disconnected
202
207
  end
203
208
 
209
+ alias close disconnect
210
+
211
+
212
+ self.handle(Protocol::Connection::Start) do |connection, frame|
213
+ connection.start_ok(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
+
204
238
  # Sends raw data through the socket
205
239
  #
206
240
  # @param [String] binary data
@@ -209,6 +243,7 @@ module AMQ
209
243
  socket.send_raw data
210
244
  end
211
245
 
246
+
212
247
  # The story about the buffering is kinda similar to EventMachine,
213
248
  # you keep receiving more than one frame in a single packet.
214
249
  #
@@ -240,32 +275,15 @@ module AMQ
240
275
 
241
276
  # @api private
242
277
  def post_init
243
- reset
244
- handshake
278
+ self.reset
279
+ self.handshake
245
280
  end
246
281
 
247
282
  # @api private
248
283
  def reset
249
284
  @chunk_buffer = ""
285
+ @frames = Array.new
250
286
  end
251
-
252
- # Returns next frame from buffer whenever possible
253
- #
254
- # @api private
255
- def get_next_frame
256
- return nil unless @chunk_buffer.size > 7 # otherwise, cannot read the length
257
- # octet + short
258
- offset = 1 + 2
259
- # length
260
- payload_length = @chunk_buffer[offset, 4].unpack('N')[0]
261
- # 4 bytes for long payload length, 1 byte final octet
262
- frame_length = offset + 4 + payload_length + 1
263
- if frame_length <= @chunk_buffer.size
264
- @chunk_buffer.slice!(0, frame_length)
265
- else
266
- nil
267
- end
268
- end
269
- end
270
- end
271
- end
287
+ end # CoolioClient
288
+ end # Client
289
+ end # AMQ
@@ -7,10 +7,6 @@ require "amq/client/framing/string/frame"
7
7
  module AMQ
8
8
  module Client
9
9
  class EventMachineClient < EM::Connection
10
- # @private
11
- class Deferrable
12
- include EventMachine::Deferrable
13
- end
14
10
 
15
11
  #
16
12
  # Behaviors
@@ -23,10 +19,7 @@ module AMQ
23
19
  # API
24
20
  #
25
21
 
26
- attr_reader :connections
27
-
28
-
29
- def self.connect(settings = nil, &block)
22
+ def self.connect(settings = {}, &block)
30
23
  @settings = Settings.configure(settings)
31
24
 
32
25
  instance = EventMachine.connect(@settings[:host], @settings[:port], self, @settings)
@@ -50,7 +43,8 @@ module AMQ
50
43
 
51
44
  if !@reconnecting
52
45
  @reconnecting = true
53
- @connections.each { |c| c.handle_connection_interruption }
46
+
47
+ self.handle_connection_interruption
54
48
  self.reset
55
49
  end
56
50
 
@@ -58,6 +52,9 @@ module AMQ
58
52
  end
59
53
 
60
54
 
55
+
56
+
57
+
61
58
  # Defines a callback that will be executed when AMQP connection is considered open:
62
59
  # client and broker has agreed on max channel identifier and maximum allowed frame
63
60
  # size and authentication succeeds. You can define more than one callback.
@@ -116,10 +113,18 @@ module AMQ
116
113
  end
117
114
 
118
115
 
116
+
117
+
119
118
  def initialize(*args)
120
119
  super(*args)
120
+
121
+ self.logger = self.class.logger
122
+
123
+ @frames = Array.new
124
+ @channels = Hash.new
125
+
121
126
  opening!
122
- @connections = Array.new
127
+
123
128
  # track TCP connection state, used to detect initial TCP connection failures.
124
129
  @tcp_connection_established = false
125
130
  @tcp_connection_failed = false
@@ -127,7 +132,7 @@ module AMQ
127
132
 
128
133
  # EventMachine::Connection's and Adapter's constructors arity
129
134
  # make it easier to use *args. MK.
130
- @settings = args.first
135
+ @settings = Settings.configure(args.first)
131
136
  @on_tcp_connection_failure = @settings[:on_tcp_connection_failure] || Proc.new { |settings|
132
137
  raise self.class.tcp_connection_failure_exception_class.new(settings)
133
138
  }
@@ -135,9 +140,11 @@ module AMQ
135
140
  raise self.class.authentication_failure_exception_class.new(settings)
136
141
  }
137
142
 
143
+ @mechanism = "PLAIN"
144
+ @locale = @settings.fetch(:locale, "en_GB")
145
+ @client_properties = Settings.client_properties.merge(@settings.fetch(:client_properties, Hash.new))
138
146
 
139
147
  self.reset
140
-
141
148
  self.set_pending_connect_timeout((@settings[:timeout] || 3).to_f) unless defined?(JRUBY_VERSION)
142
149
 
143
150
  if self.heartbeat_interval > 0
@@ -147,7 +154,17 @@ module AMQ
147
154
  end # initialize(*args)
148
155
 
149
156
 
150
- alias send_raw send_data
157
+
158
+ # For EventMachine adapter, this is a no-op.
159
+ # @api public
160
+ def establish_connection(settings)
161
+ # Unfortunately there doesn't seem to be any sane way
162
+ # how to get EventMachine connect to the instance level.
163
+ end
164
+
165
+ alias close disconnect
166
+
167
+
151
168
 
152
169
  # Whether we are in authentication state (after TCP connection was estabilished
153
170
  # but before broker authenticated us).
@@ -165,12 +182,8 @@ module AMQ
165
182
  @tcp_connection_established
166
183
  end # tcp_connection_established?
167
184
 
168
- # For EventMachine adapter, this is a no-op.
169
- # @api public
170
- def establish_connection(settings)
171
- # Unfortunately there doesn't seem to be any sane way
172
- # how to get EventMachine connect to the instance level.
173
- end
185
+
186
+
174
187
 
175
188
 
176
189
 
@@ -178,6 +191,13 @@ module AMQ
178
191
  # Implementation
179
192
  #
180
193
 
194
+ # Backwards compatibility with 0.7.0.a25. MK.
195
+ Deferrable = EventMachine::DefaultDeferrable
196
+
197
+
198
+ alias send_raw send_data
199
+
200
+
181
201
  # EventMachine reactor callback. Is run when TCP connection is estabilished
182
202
  # but before resumption of the network loop. Note that this includes cases
183
203
  # when TCP connection has failed.
@@ -196,6 +216,8 @@ module AMQ
196
216
  raise error
197
217
  end # post_init
198
218
 
219
+
220
+
199
221
  # Called by EventMachine reactor once TCP connection is successfully estabilished.
200
222
  # @private
201
223
  def connection_completed
@@ -236,7 +258,7 @@ module AMQ
236
258
  closing!
237
259
  @tcp_connection_established = false
238
260
 
239
- @connections.each { |c| c.handle_connection_interruption }
261
+ self.handle_connection_interruption
240
262
  @disconnection_deferrable.succeed
241
263
 
242
264
  closed!
@@ -283,38 +305,43 @@ module AMQ
283
305
  def disconnection_successful
284
306
  @disconnection_deferrable.succeed
285
307
 
286
- self.close_connection
308
+ # true for "after writing buffered data"
309
+ self.close_connection(true)
287
310
  self.reset
288
311
  closed!
289
312
  end # disconnection_successful
290
313
 
291
314
 
292
- # Called when previously established TCP connection fails.
293
- # @api public
294
- def tcp_connection_lost
295
- @on_tcp_connection_loss.call(self, @settings) if @on_tcp_connection_loss
315
+
316
+
317
+
318
+ self.handle(Protocol::Connection::Start) do |connection, frame|
319
+ connection.start_ok(frame.decode_payload)
296
320
  end
297
321
 
298
- # Called when initial TCP connection fails.
299
- # @api public
300
- def tcp_connection_failed
301
- @on_tcp_connection_failure.call(@settings) if @on_tcp_connection_failure
322
+ self.handle(Protocol::Connection::Tune) do |connection, frame|
323
+ connection.handle_tune(frame.decode_payload)
324
+
325
+ connection.open(connection.vhost)
302
326
  end
303
327
 
328
+ self.handle(Protocol::Connection::OpenOk) do |connection, frame|
329
+ connection.handle_open_ok(frame.decode_payload)
330
+ end
304
331
 
305
- protected
332
+ self.handle(Protocol::Connection::Close) do |connection, frame|
333
+ connection.handle_close(frame.decode_payload)
334
+ end
306
335
 
307
- def handshake(mechanism = "PLAIN", response = nil, locale = "en_GB")
308
- username = @settings[:user] || @settings[:username]
309
- password = @settings[:pass] || @settings[:password]
336
+ self.handle(Protocol::Connection::CloseOk) do |connection, frame|
337
+ connection.handle_close_ok(frame.decode_payload)
338
+ end
310
339
 
311
- # self.logger.info "[authentication] Credentials are #{username}/#{'*' * password.bytesize}"
312
340
 
313
- self.connection = AMQ::Client::Connection.new(self, mechanism, self.encode_credentials(username, password), locale)
314
341
 
315
- @authenticating = true
316
- self.send_preamble
317
- end
342
+
343
+ protected
344
+
318
345
 
319
346
  def reset
320
347
  @size = 0
@@ -322,8 +349,8 @@ module AMQ
322
349
  @frames = Array.new
323
350
 
324
351
  @chunk_buffer = ""
325
- @connection_deferrable = Deferrable.new
326
- @disconnection_deferrable = Deferrable.new
352
+ @connection_deferrable = EventMachine::DefaultDeferrable.new
353
+ @disconnection_deferrable = EventMachine::DefaultDeferrable.new
327
354
 
328
355
  # used to track down whether authentication succeeded. AMQP 0.9.1 dictates
329
356
  # that on authentication failure broker must close TCP connection without sending
@@ -332,26 +359,6 @@ module AMQ
332
359
  @authenticating = false
333
360
  end
334
361
 
335
- # @see http://tools.ietf.org/rfc/rfc2595.txt RFC 2595
336
- def encode_credentials(username, password)
337
- "\0#{username}\0#{password}"
338
- end # encode_credentials(username, password)
339
-
340
- def get_next_frame
341
- return unless @chunk_buffer.size > 7 # otherwise, cannot read the length
342
- # octet + short
343
- offset = 3 # 1 + 2
344
- # length
345
- payload_length = @chunk_buffer[offset, 4].unpack('N')[0]
346
- # 5: 4 bytes for long payload length, 1 byte final octet
347
- frame_length = offset + 5 + payload_length
348
- if frame_length <= @chunk_buffer.size
349
- @chunk_buffer.slice!(0, frame_length)
350
- else
351
- nil
352
- end
353
- end # get_next_frame
354
-
355
362
  def upgrade_to_tls_if_necessary
356
363
  tls_options = @settings[:ssl]
357
364
 
@@ -33,23 +33,32 @@ module AMQ
33
33
 
34
34
 
35
35
  def exec_callback(name, *args, &block)
36
- callbacks = Array(self.callbacks[name])
37
- callbacks.map { |c| c.call(*args, &block) } if callbacks.any?
36
+ list = Array(self.callbacks[name])
37
+ if list.any?
38
+ list.each { |c| c.call(*args, &block) }
39
+ end
38
40
  end
39
41
 
40
42
  def exec_callback_once(name, *args, &block)
41
- callbacks = Array(self.callbacks.delete(name))
42
- callbacks.map { |c| c.call(*args, &block) } if callbacks.any?
43
+ list = (self.callbacks.delete(name) || Array.new)
44
+ if list.any?
45
+ list.each { |c| c.call(*args, &block) }
46
+ end
43
47
  end
44
48
 
45
49
  def exec_callback_yielding_self(name, *args, &block)
46
- callbacks = Array(self.callbacks[name])
47
- callbacks.map { |c| c.call(self, *args, &block) } if callbacks.any?
50
+ list = Array(self.callbacks[name])
51
+ if list.any?
52
+ list.each { |c| c.call(self, *args, &block) }
53
+ end
48
54
  end
49
55
 
50
56
  def exec_callback_once_yielding_self(name, *args, &block)
51
- callbacks = Array(self.callbacks.delete(name))
52
- callbacks.map { |c| c.call(self, *args, &block) } if callbacks.any?
57
+ list = (self.callbacks.delete(name) || Array.new)
58
+
59
+ if list.any?
60
+ list.each { |c| c.call(self, *args, &block) }
61
+ end
53
62
  end
54
63
 
55
64
  def has_callback?(name)
@@ -57,4 +66,4 @@ module AMQ
57
66
  end # has_callback?
58
67
  end # Callbacks
59
68
  end # Client
60
- end # AMQ
69
+ end # AMQ