bunny 0.5.2 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -35,29 +35,29 @@ Sets up a Bunny::Client object ready for connection to a broker/server. _Client_
35
35
  =end
36
36
 
37
37
  def initialize(opts = {})
38
+ super
38
39
  @spec = '0-9-1'
39
- @host = opts[:host] || 'localhost'
40
40
  @port = opts[:port] || Qrack::Protocol09::PORT
41
- @user = opts[:user] || 'guest'
42
- @pass = opts[:pass] || 'guest'
43
- @vhost = opts[:vhost] || '/'
44
- @logfile = opts[:logfile] || nil
45
- @logging = opts[:logging] || false
46
- @status = :not_connected
47
- @frame_max = opts[:frame_max] || 131072
48
- @channel_max = opts[:channel_max] || 5
49
- @heartbeat = opts[:heartbeat] || 0
50
- @logger = nil
51
- create_logger if @logging
52
- @channels = []
53
- # Create channel 0
54
- @channel = Bunny::Channel09.new(self, true)
55
- @exchanges = {}
56
- @queues = {}
57
- @heartbeat_in = false
58
- @connecting = false
59
41
  end
60
42
 
43
+ def close_connection
44
+ # Set client channel to zero
45
+ switch_channel(0)
46
+
47
+ send_frame(
48
+ Qrack::Protocol09::Connection::Close.new(:reply_code => 200, :reply_text => 'Goodbye', :class_id => 0, :method_id => 0)
49
+ )
50
+ raise Bunny::ProtocolError, "Error closing connection" unless next_method.is_a?(Qrack::Protocol09::Connection::CloseOk)
51
+ end
52
+
53
+ def create_channel
54
+ channels.each do |c|
55
+ return c if (!c.open? and c.number != 0)
56
+ end
57
+ # If no channel to re-use instantiate new one
58
+ Bunny::Channel09.new(self)
59
+ end
60
+
61
61
  =begin rdoc
62
62
 
63
63
  === DESCRIPTION:
@@ -89,6 +89,97 @@ Exchange
89
89
  return exchanges[name] if exchanges.has_key?(name)
90
90
  exchanges[name] ||= Bunny::Exchange09.new(self, name, opts)
91
91
  end
92
+
93
+ def init_connection
94
+ write(Qrack::Protocol09::HEADER)
95
+ write([0, Qrack::Protocol09::VERSION_MAJOR, Qrack::Protocol09::VERSION_MINOR, Qrack::Protocol09::REVISION].pack('C4'))
96
+
97
+ frame = next_frame
98
+ if frame.nil? or !frame.payload.is_a?(Qrack::Protocol09::Connection::Start)
99
+ raise Bunny::ProtocolError, 'Connection initiation failed'
100
+ end
101
+ end
102
+
103
+ def next_frame(opts = {})
104
+ frame = nil
105
+
106
+ case
107
+ when channel.frame_buffer.size > 0
108
+ frame = channel.frame_buffer.shift
109
+ when opts.has_key?(:timeout)
110
+ Timeout::timeout(opts[:timeout], Qrack::ClientTimeout) do
111
+ frame = Qrack::Transport09::Frame.parse(buffer)
112
+ end
113
+ else
114
+ frame = Qrack::Transport09::Frame.parse(buffer)
115
+ end
116
+
117
+ @logger.info("received") { frame } if @logging
118
+
119
+ raise Bunny::ConnectionError, 'No connection to server' if (frame.nil? and !connecting?)
120
+
121
+ # Monitor server activity and discard heartbeats
122
+ @message_in = true
123
+
124
+ case
125
+ when frame.is_a?(Qrack::Transport09::Heartbeat)
126
+ next_frame(opts)
127
+ when frame.nil?
128
+ frame
129
+ when ((frame.channel != channel.number) and (frame.channel != 0))
130
+ channel.frame_buffer << frame
131
+ next_frame(opts)
132
+ else
133
+ frame
134
+ end
135
+
136
+ end
137
+
138
+ =begin rdoc
139
+
140
+ === DESCRIPTION:
141
+
142
+ Requests a specific quality of service. The QoS can be specified for the current channel
143
+ or for all channels on the connection. The particular properties and semantics of a QoS
144
+ method always depend on the content class semantics. Though the QoS method could in principle
145
+ apply to both peers, it is currently meaningful only for the server.
146
+
147
+ ==== Options:
148
+
149
+ * <tt>:prefetch_size => size in no. of octets (default = 0)</tt> - The client can request that
150
+ messages be sent in advance so that when the client finishes processing a message, the following
151
+ message is already held locally, rather than needing to be sent down the channel. Prefetching gives
152
+ a performance improvement. This field specifies the prefetch window size in octets. The server
153
+ will send a message in advance if it is equal to or smaller in size than the available prefetch
154
+ size (and also falls into other prefetch limits). May be set to zero, meaning "no specific limit",
155
+ although other prefetch limits may still apply. The prefetch-size is ignored if the no-ack option
156
+ is set.
157
+ * <tt>:prefetch_count => no. messages (default = 1)</tt> - Specifies a prefetch window in terms
158
+ of whole messages. This field may be used in combination with the prefetch-size field; a message
159
+ will only be sent in advance if both prefetch windows (and those at the channel and connection level)
160
+ allow it. The prefetch-count is ignored if the no-ack option is set.
161
+ * <tt>:global => true or false (_default_)</tt> - By default the QoS settings apply to the current channel only. If set to
162
+ true, they are applied to the entire connection.
163
+
164
+ ==== RETURNS:
165
+
166
+ <tt>:qos_ok</tt> if successful.
167
+
168
+ =end
169
+
170
+ def qos(opts = {})
171
+
172
+ send_frame(
173
+ Qrack::Protocol09::Basic::Qos.new({ :prefetch_size => 0, :prefetch_count => 1, :global => false }.merge(opts))
174
+ )
175
+
176
+ raise Bunny::ProtocolError,
177
+ "Error specifying Quality of Service" unless
178
+ next_method.is_a?(Qrack::Protocol09::Basic::QosOk)
179
+
180
+ # return confirmation
181
+ :qos_ok
182
+ end
92
183
 
93
184
  =begin rdoc
94
185
 
@@ -134,130 +225,54 @@ Queue
134
225
 
135
226
  Bunny::Queue09.new(self, name, opts)
136
227
  end
137
-
138
- def send_heartbeat
139
- # Create a new heartbeat frame
140
- hb = Qrack::Transport09::Heartbeat.new('')
141
- # Channel 0 must be used
142
- switch_channel(0) if @channel.number > 0
143
- # Send the heartbeat to server
144
- send_frame(hb)
145
- end
146
-
147
- def send_frame(*args)
148
- args.each do |data|
149
- data = data.to_frame(channel.number) unless data.is_a?(Qrack::Transport09::Frame)
150
- data.channel = channel.number
151
-
152
- @logger.info("send") { data } if @logging
153
- write(data.to_s)
154
- end
155
- nil
156
- end
157
-
158
- def next_frame(opts = {})
159
- secs = opts[:timeout] || 0
160
-
161
- begin
162
- Timeout::timeout(secs) do
163
- @frame = Qrack::Transport09::Frame.parse(buffer)
164
- end
165
- rescue Timeout::Error
166
- return :timed_out
167
- end
168
-
169
- @logger.info("received") { @frame } if @logging
170
-
171
- raise Bunny::ConnectionError, 'No connection to server' if (@frame.nil? and !connecting?)
172
-
173
- if @frame.is_a?(Qrack::Transport09::Heartbeat)
174
- @heartbeat_in = true
175
- next_frame
176
- end
177
-
178
- @frame
179
- end
180
-
181
- def next_payload
182
- frame = next_frame
183
- frame.payload
184
- end
185
-
186
- alias next_method next_payload
187
-
188
- =begin rdoc
189
-
190
- === DESCRIPTION:
191
-
192
- Checks to see whether or not an undeliverable message has been returned as a result of a publish
193
- with the <tt>:immediate</tt> or <tt>:mandatory</tt> options.
194
-
195
- ==== OPTIONS:
196
-
197
- * <tt>:timeout => number of seconds (default = 0.1) - The method will wait for a return
198
- message until this timeout interval is reached.
199
-
200
- ==== RETURNS:
201
-
202
- <tt>:no_return</tt> if message was not returned before timeout .
203
- <tt>{:header, :return_details, :payload}</tt> if message is returned. <tt>:return_details</tt> is
204
- a hash <tt>{:reply_code, :reply_text, :exchange, :routing_key}</tt>.
205
-
206
- =end
207
-
208
- def returned_message(opts = {})
209
- secs = opts[:timeout] || 0.1
210
- frame = next_frame(:timeout => secs)
211
-
212
- if frame.is_a?(Symbol)
213
- return :no_return if frame == :timed_out
214
- end
215
-
216
- method = frame.payload
217
- header = next_payload
218
- msg = next_payload
219
- raise Bunny::MessageError, 'unexpected length' if msg.length < header.size
220
-
221
- # Return the message and related info
222
- {:header => header, :payload => msg, :return_details => method.arguments}
223
- end
224
-
228
+
225
229
  =begin rdoc
226
230
 
227
231
  === DESCRIPTION:
228
232
 
229
- Closes the current communication channel and connection. If an error occurs a
230
- _Bunny_::_ProtocolError_ is raised. If successful, _Client_._status_ is set to <tt>:not_connected</tt>.
233
+ Asks the broker to redeliver all unacknowledged messages on a specified channel. Zero or
234
+ more messages may be redelivered.
231
235
 
232
- ==== RETURNS:
236
+ ==== Options:
233
237
 
234
- <tt>:not_connected</tt> if successful.
238
+ * <tt>:requeue => true or false (_default_)</tt> - If set to _false_, the message will be
239
+ redelivered to the original recipient. If set to _true_, the server will attempt to requeue
240
+ the message, potentially then delivering it to an alternative subscriber.
235
241
 
236
242
  =end
237
243
 
238
- def close
239
- # Close all active channels
240
- channels.each do |c|
241
- c.close if c.open?
242
- end
244
+ def recover(opts = {})
243
245
 
244
- # Close connection to AMQP server
245
- close_connection
246
+ send_frame(
247
+ Qrack::Protocol09::Basic::Recover.new({ :requeue => false }.merge(opts))
248
+ )
246
249
 
247
- # Close TCP socket
248
- close_socket
249
- end
250
+ end
251
+
252
+ def send_frame(*args)
253
+ args.each do |data|
254
+ data = data.to_frame(channel.number) unless data.is_a?(Qrack::Transport09::Frame)
255
+ data.channel = channel.number
250
256
 
251
- alias stop close
257
+ @logger.info("send") { data } if @logging
258
+ write(data.to_s)
252
259
 
253
- def read(*args)
254
- send_command(:read, *args)
255
- end
260
+ # Monitor client activity for heartbeat purposes
261
+ @message_out = true
262
+ end
256
263
 
257
- def write(*args)
258
- send_command(:write, *args)
264
+ nil
259
265
  end
260
-
266
+
267
+ def send_heartbeat
268
+ # Create a new heartbeat frame
269
+ hb = Qrack::Transport09::Heartbeat.new('')
270
+ # Channel 0 must be used
271
+ switch_channel(0) if @channel.number > 0
272
+ # Send the heartbeat to server
273
+ send_frame(hb)
274
+ end
275
+
261
276
  =begin rdoc
262
277
 
263
278
  === DESCRIPTION:
@@ -298,68 +313,51 @@ _Bunny_::_ProtocolError_ is raised. If successful, _Client_._status_ is set to <
298
313
  =begin rdoc
299
314
 
300
315
  === DESCRIPTION:
316
+ This method commits all messages published and acknowledged in
317
+ the current transaction. A new transaction starts immediately
318
+ after a commit.
301
319
 
302
- Asks the broker to redeliver all unacknowledged messages on a specified channel. Zero or
303
- more messages may be redelivered.
304
-
305
- ==== Options:
320
+ ==== RETURNS:
306
321
 
307
- * <tt>:requeue => true or false (_default_)</tt> - If set to _false_, the message will be
308
- redelivered to the original recipient. If set to _true_, the server will attempt to requeue
309
- the message, potentially then delivering it to an alternative subscriber.
322
+ <tt>:commit_ok</tt> if successful.
310
323
 
311
324
  =end
312
325
 
313
- def recover(opts = {})
326
+ def tx_commit
327
+ send_frame(Qrack::Protocol09::Tx::Commit.new())
314
328
 
315
- send_frame(
316
- Qrack::Protocol09::Basic::Recover.new({ :requeue => false }.merge(opts))
317
- )
329
+ raise Bunny::ProtocolError,
330
+ "Error commiting transaction" unless
331
+ next_method.is_a?(Qrack::Protocol09::Tx::CommitOk)
318
332
 
319
- end
320
-
333
+ # return confirmation
334
+ :commit_ok
335
+ end
336
+
321
337
  =begin rdoc
322
338
 
323
339
  === DESCRIPTION:
340
+ This method abandons all messages published and acknowledged in
341
+ the current transaction. A new transaction starts immediately
342
+ after a rollback.
324
343
 
325
- Requests a specific quality of service. The QoS can be specified for the current channel
326
- or for all channels on the connection. The particular properties and semantics of a QoS
327
- method always depend on the content class semantics. Though the QoS method could in principle
328
- apply to both peers, it is currently meaningful only for the server.
344
+ ==== RETURNS:
329
345
 
330
- ==== Options:
331
-
332
- * <tt>:prefetch_size => size in no. of octets (default = 0)</tt> - The client can request that
333
- messages be sent in advance so that when the client finishes processing a message, the following
334
- message is already held locally, rather than needing to be sent down the channel. Prefetching gives
335
- a performance improvement. This field specifies the prefetch window size in octets. The server
336
- will send a message in advance if it is equal to or smaller in size than the available prefetch
337
- size (and also falls into other prefetch limits). May be set to zero, meaning "no specific limit",
338
- although other prefetch limits may still apply. The prefetch-size is ignored if the no-ack option
339
- is set.
340
- * <tt>:prefetch_count => no. messages (default = 1)</tt> - Specifies a prefetch window in terms
341
- of whole messages. This field may be used in combination with the prefetch-size field; a message
342
- will only be sent in advance if both prefetch windows (and those at the channel and connection level)
343
- allow it. The prefetch-count is ignored if the no-ack option is set.
344
- * <tt>:global => true or false (_default_)</tt> - By default the QoS settings apply to the current channel only. If set to
345
- true, they are applied to the entire connection.
346
+ <tt>:rollback_ok</tt> if successful.
346
347
 
347
348
  =end
348
349
 
349
- def qos(opts = {})
350
-
351
- send_frame(
352
- Qrack::Protocol09::Basic::Qos.new({ :prefetch_size => 0, :prefetch_count => 1, :global => false }.merge(opts))
353
- )
354
-
355
- raise Bunny::ProtocolError,
356
- "Error specifying Quality of Service" unless
357
- next_method.is_a?(Qrack::Protocol09::Basic::QosOk)
350
+ def tx_rollback
351
+ send_frame(Qrack::Protocol09::Tx::Rollback.new())
358
352
 
359
- # return confirmation
360
- :qos_ok
361
- end
353
+ raise Bunny::ProtocolError,
354
+ "Error rolling back transaction" unless
355
+ next_method.is_a?(Qrack::Protocol09::Tx::RollbackOk)
362
356
 
357
+ # return confirmation
358
+ :rollback_ok
359
+ end
360
+
363
361
  =begin rdoc
364
362
 
365
363
  === DESCRIPTION:
@@ -367,6 +365,10 @@ This method sets the channel to use standard transactions. The
367
365
  client must use this method at least once on a channel before
368
366
  using the Commit or Rollback methods.
369
367
 
368
+ ==== RETURNS:
369
+
370
+ <tt>:select_ok</tt> if successful.
371
+
370
372
  =end
371
373
 
372
374
  def tx_select
@@ -380,82 +382,6 @@ using the Commit or Rollback methods.
380
382
  :select_ok
381
383
  end
382
384
 
383
- =begin rdoc
384
-
385
- === DESCRIPTION:
386
- This method commits all messages published and acknowledged in
387
- the current transaction. A new transaction starts immediately
388
- after a commit.
389
-
390
- =end
391
-
392
- def tx_commit
393
- send_frame(Qrack::Protocol09::Tx::Commit.new())
394
-
395
- raise Bunny::ProtocolError,
396
- "Error commiting transaction" unless
397
- next_method.is_a?(Qrack::Protocol09::Tx::CommitOk)
398
-
399
- # return confirmation
400
- :commit_ok
401
- end
402
-
403
- =begin rdoc
404
-
405
- === DESCRIPTION:
406
- This method abandons all messages published and acknowledged in
407
- the current transaction. A new transaction starts immediately
408
- after a rollback.
409
-
410
- =end
411
-
412
- def tx_rollback
413
- send_frame(Qrack::Protocol09::Tx::Rollback.new())
414
-
415
- raise Bunny::ProtocolError,
416
- "Error rolling back transaction" unless
417
- next_method.is_a?(Qrack::Protocol09::Tx::RollbackOk)
418
-
419
- # return confirmation
420
- :rollback_ok
421
- end
422
-
423
- def logging=(bool)
424
- @logging = bool
425
- create_logger if @logging
426
- end
427
-
428
- def create_channel
429
- channels.each do |c|
430
- return c if (!c.open? and c.number != 0)
431
- end
432
- # If no channel to re-use instantiate new one
433
- Bunny::Channel09.new(self)
434
- end
435
-
436
- def switch_channel(chann)
437
- if (0...channels.size).include? chann
438
- @channel = channels[chann]
439
- chann
440
- else
441
- raise RuntimeError, "Invalid channel number - #{chann}"
442
- end
443
- end
444
-
445
- def connecting?
446
- connecting
447
- end
448
-
449
- def init_connection
450
- write(Qrack::Protocol09::HEADER)
451
- write([0, Qrack::Protocol09::VERSION_MAJOR, Qrack::Protocol09::VERSION_MINOR, Qrack::Protocol09::REVISION].pack('C4'))
452
-
453
- frame = next_frame
454
- if frame.nil? or !frame.payload.is_a?(Qrack::Protocol09::Connection::Start)
455
- raise Bunny::ProtocolError, 'Connection initiation failed'
456
- end
457
- end
458
-
459
385
  def open_connection
460
386
  send_frame(
461
387
  Qrack::Protocol09::Connection::StartOk.new(
@@ -483,16 +409,6 @@ after a rollback.
483
409
 
484
410
  raise Bunny::ProtocolError, 'Cannot open connection' unless next_method.is_a?(Qrack::Protocol09::Connection::OpenOk)
485
411
  end
486
-
487
- def close_connection
488
- # Set client channel to zero
489
- switch_channel(0)
490
-
491
- send_frame(
492
- Qrack::Protocol09::Connection::Close.new(:reply_code => 200, :reply_text => 'Goodbye', :class_id => 0, :method_id => 0)
493
- )
494
- raise Bunny::ProtocolError, "Error closing connection" unless next_method.is_a?(Qrack::Protocol09::Connection::CloseOk)
495
- end
496
412
 
497
413
  private
498
414
 
@@ -500,47 +416,5 @@ after a rollback.
500
416
  @buffer ||= Qrack::Transport09::Buffer.new(self)
501
417
  end
502
418
 
503
- def send_command(cmd, *args)
504
- begin
505
- raise Bunny::ConnectionError, 'No connection - socket has not been created' if !@socket
506
- @socket.__send__(cmd, *args)
507
- rescue Errno::EPIPE, IOError => e
508
- raise Bunny::ServerDownError, e.message
509
- end
510
- end
511
-
512
- def socket
513
- return @socket if @socket and (@status == :connected) and not @socket.closed?
514
-
515
- begin
516
- # Attempt to connect.
517
- @socket = timeout(CONNECT_TIMEOUT) do
518
- TCPSocket.new(host, port)
519
- end
520
-
521
- if Socket.constants.include? 'TCP_NODELAY'
522
- @socket.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
523
- end
524
- rescue => e
525
- @status = :not_connected
526
- raise Bunny::ServerDownError, e.message
527
- end
528
-
529
- @socket
530
- end
531
-
532
- def close_socket(reason=nil)
533
- # Close the socket. The server is not considered dead.
534
- @socket.close if @socket and not @socket.closed?
535
- @socket = nil
536
- @status = :not_connected
537
- end
538
-
539
- def create_logger
540
- @logfile ? @logger = Logger.new("#{logfile}") : @logger = Logger.new(STDOUT)
541
- @logger.level = Logger::INFO
542
- @logger.datetime_format = "%Y-%m-%d %H:%M:%S"
543
- end
544
-
545
419
  end
546
420
  end