sparqcode_bunny 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/.gitignore +10 -0
  2. data/.rspec +3 -0
  3. data/.travis.yml +15 -0
  4. data/.yardopts +9 -0
  5. data/CHANGELOG +27 -0
  6. data/Gemfile +39 -0
  7. data/LICENSE +21 -0
  8. data/README.textile +82 -0
  9. data/Rakefile +14 -0
  10. data/bunny.gemspec +43 -0
  11. data/examples/simple_08.rb +32 -0
  12. data/examples/simple_09.rb +32 -0
  13. data/examples/simple_ack_08.rb +35 -0
  14. data/examples/simple_ack_09.rb +35 -0
  15. data/examples/simple_consumer_08.rb +55 -0
  16. data/examples/simple_consumer_09.rb +55 -0
  17. data/examples/simple_fanout_08.rb +41 -0
  18. data/examples/simple_fanout_09.rb +41 -0
  19. data/examples/simple_headers_08.rb +42 -0
  20. data/examples/simple_headers_09.rb +42 -0
  21. data/examples/simple_publisher_08.rb +29 -0
  22. data/examples/simple_publisher_09.rb +29 -0
  23. data/examples/simple_topic_08.rb +61 -0
  24. data/examples/simple_topic_09.rb +61 -0
  25. data/ext/amqp-0.8.json +616 -0
  26. data/ext/amqp-0.9.1.json +388 -0
  27. data/ext/config.yml +4 -0
  28. data/ext/qparser.rb +469 -0
  29. data/lib/bunny/channel08.rb +39 -0
  30. data/lib/bunny/channel09.rb +39 -0
  31. data/lib/bunny/client08.rb +472 -0
  32. data/lib/bunny/client09.rb +374 -0
  33. data/lib/bunny/consumer.rb +35 -0
  34. data/lib/bunny/exchange08.rb +171 -0
  35. data/lib/bunny/exchange09.rb +159 -0
  36. data/lib/bunny/queue08.rb +403 -0
  37. data/lib/bunny/queue09.rb +325 -0
  38. data/lib/bunny/subscription08.rb +87 -0
  39. data/lib/bunny/subscription09.rb +87 -0
  40. data/lib/bunny/system_timer.rb +14 -0
  41. data/lib/bunny/version.rb +5 -0
  42. data/lib/bunny.rb +109 -0
  43. data/lib/qrack/amq-client-url.rb +165 -0
  44. data/lib/qrack/channel.rb +20 -0
  45. data/lib/qrack/client.rb +235 -0
  46. data/lib/qrack/errors.rb +5 -0
  47. data/lib/qrack/protocol/protocol08.rb +134 -0
  48. data/lib/qrack/protocol/protocol09.rb +135 -0
  49. data/lib/qrack/protocol/spec08.rb +828 -0
  50. data/lib/qrack/protocol/spec09.rb +524 -0
  51. data/lib/qrack/qrack08.rb +20 -0
  52. data/lib/qrack/qrack09.rb +20 -0
  53. data/lib/qrack/queue.rb +40 -0
  54. data/lib/qrack/subscription.rb +112 -0
  55. data/lib/qrack/transport/buffer08.rb +278 -0
  56. data/lib/qrack/transport/buffer09.rb +280 -0
  57. data/lib/qrack/transport/frame08.rb +117 -0
  58. data/lib/qrack/transport/frame09.rb +97 -0
  59. data/spec/spec_08/bunny_spec.rb +77 -0
  60. data/spec/spec_08/connection_spec.rb +25 -0
  61. data/spec/spec_08/exchange_spec.rb +173 -0
  62. data/spec/spec_08/queue_spec.rb +235 -0
  63. data/spec/spec_09/amqp_url_spec.rb +19 -0
  64. data/spec/spec_09/bunny_spec.rb +76 -0
  65. data/spec/spec_09/connection_spec.rb +29 -0
  66. data/spec/spec_09/exchange_spec.rb +173 -0
  67. data/spec/spec_09/queue_spec.rb +248 -0
  68. metadata +151 -0
@@ -0,0 +1,472 @@
1
+ # encoding: utf-8
2
+
3
+ module Bunny
4
+
5
+ =begin rdoc
6
+
7
+ === DESCRIPTION:
8
+
9
+ The Client class provides the major Bunny API methods.
10
+
11
+ =end
12
+
13
+ class Client < Qrack::Client
14
+
15
+ attr_accessor :ticket
16
+
17
+ =begin rdoc
18
+
19
+ === DESCRIPTION:
20
+
21
+ Sets up a Bunny::Client object ready for connection to a broker/server. _Client_._status_ is set to
22
+ <tt>:not_connected</tt>.
23
+
24
+ ==== OPTIONS:
25
+
26
+ * <tt>:host => '_hostname_' (default = 'localhost')</tt>
27
+ * <tt>:port => _portno_ (default = 5672 or 5671 if :ssl set to true)</tt>
28
+ * <tt>:vhost => '_vhostname_' (default = '/')</tt>
29
+ * <tt>:user => '_username_' (default = 'guest')</tt>
30
+ * <tt>:pass => '_password_' (default = 'guest')</tt>
31
+ * <tt>:ssl => true or false (default = false)</tt> - If set to _true_, ssl
32
+ encryption will be used and port will default to 5671.
33
+ * <tt>:verify_ssl => true or false (default = true)</tt> - If ssl is enabled,
34
+ this will cause OpenSSL to validate the server certificate unless this
35
+ parameter is set to _false_.
36
+ * <tt>:logfile => '_logfilepath_' (default = nil)</tt>
37
+ * <tt>:logging => true or false (_default_)</tt> - If set to _true_, session information is sent
38
+ to STDOUT if <tt>:logfile</tt> has not been specified. Otherwise, session information is written to
39
+ <tt>:logfile</tt>.
40
+ * <tt>:insist => true or false (_default_)</tt> - In a configuration with multiple load-sharing
41
+ servers, the server may respond to a Connection::Open method with a Connection::Redirect. The insist
42
+ option, if set to _true_, tells the server that the client is insisting on a connection to the
43
+ specified server.
44
+ * <tt>:frame_max => maximum frame size in bytes (default = 131072)</tt>
45
+ * <tt>:channel_max => maximum number of channels (default = 0 no maximum)</tt>
46
+ * <tt>:heartbeat => number of seconds (default = 0 no heartbeat)</tt>
47
+ * <tt>:connect_timeout => number of seconds before Qrack::ConnectionTimeout is raised (default = 5)</tt>
48
+
49
+ =end
50
+
51
+ def initialize(connection_string_or_opts = Hash.new, opts = Hash.new)
52
+ super
53
+ @spec = '0-8'
54
+ @port = self.__opts__[:port] || (self.__opts__[:ssl] ? Qrack::Protocol::SSL_PORT : Qrack::Protocol::PORT)
55
+ @insist = self.__opts__[:insist]
56
+ end
57
+
58
+ =begin rdoc
59
+
60
+ === DESCRIPTION:
61
+
62
+ Checks response from AMQP methods and takes appropriate action
63
+
64
+ =end
65
+
66
+ def check_response(received_method, expected_method, err_msg, err_class = Bunny::ProtocolError)
67
+ case
68
+ when received_method.is_a?(Qrack::Protocol::Connection::Close)
69
+ # Clean up the socket
70
+ close_socket
71
+
72
+ raise Bunny::ForcedConnectionCloseError, "Error Reply Code: #{received_method.reply_code}\nError Reply Text: #{received_method.reply_text}"
73
+
74
+ when received_method.is_a?(Qrack::Protocol::Channel::Close)
75
+ # Clean up the channel
76
+ channel.active = false
77
+
78
+ raise Bunny::ForcedChannelCloseError, "Error Reply Code: #{received_method.reply_code}\nError Reply Text: #{received_method.reply_text}"
79
+
80
+ when !received_method.is_a?(expected_method)
81
+ raise err_class, err_msg
82
+
83
+ else
84
+ :response_ok
85
+ end
86
+ end
87
+
88
+ def close_connection
89
+ # Set client channel to zero
90
+ switch_channel(0)
91
+
92
+ send_frame(Qrack::Protocol::Connection::Close.new(:reply_code => 200, :reply_text => 'Goodbye', :class_id => 0, :method_id => 0))
93
+
94
+ method = next_method
95
+
96
+ check_response(method, Qrack::Protocol::Connection::CloseOk, "Error closing connection")
97
+ end
98
+
99
+ def create_channel
100
+ channels.each do |c|
101
+ return c if (!c.open? and c.number != 0)
102
+ end
103
+ # If no channel to re-use instantiate new one
104
+ Bunny::Channel.new(self)
105
+ end
106
+
107
+ =begin rdoc
108
+
109
+ === DESCRIPTION:
110
+
111
+ Declares an exchange to the broker/server. If the exchange does not exist, a new one is created
112
+ using the arguments passed in. If the exchange already exists, the existing object is returned.
113
+ If an error occurs a _Bunny_::_ProtocolError_ is raised.
114
+
115
+ ==== OPTIONS:
116
+
117
+ * <tt>:type => one of :direct (_default_), :fanout, :topic, :headers</tt>
118
+ * <tt>:passive => true or false</tt> - If set to _true_, the server will not create the exchange.
119
+ The client can use this to check whether an exchange exists without modifying the server state.
120
+ * <tt>:durable => true or false (_default_)</tt> - If set to _true_ when creating a new exchange, the exchange
121
+ will be marked as durable. Durable exchanges remain active when a server restarts. Non-durable
122
+ exchanges (transient exchanges) are purged if/when a server restarts.
123
+ * <tt>:auto_delete => true or false (_default_)</tt> - If set to _true_, the exchange is deleted
124
+ when all queues have finished using it.
125
+ * <tt>:nowait => true or false (_default_)</tt> - Ignored by Bunny, always _false_.
126
+
127
+ ==== RETURNS:
128
+
129
+ Exchange
130
+
131
+ =end
132
+
133
+ def exchange(name, opts = {})
134
+ exchanges[name] || Bunny::Exchange.new(self, name, opts)
135
+ end
136
+
137
+ def init_connection
138
+ write(Qrack::Protocol::HEADER)
139
+ write([1, 1, Qrack::Protocol::VERSION_MAJOR, Qrack::Protocol::VERSION_MINOR].pack('C4'))
140
+
141
+ frame = next_frame
142
+ if frame.nil? or !frame.payload.is_a?(Qrack::Protocol::Connection::Start)
143
+ raise Bunny::ProtocolError, 'Connection initiation failed'
144
+ end
145
+ end
146
+
147
+ def next_frame(opts = {})
148
+ frame = nil
149
+
150
+ case
151
+ when channel.frame_buffer.size > 0
152
+ frame = channel.frame_buffer.shift
153
+ when (timeout = opts[:timeout]) && timeout > 0
154
+ Bunny::Timer::timeout(timeout, Qrack::FrameTimeout) do
155
+ frame = Qrack::Transport::Frame.parse(buffer)
156
+ end
157
+ else
158
+ frame = Qrack::Transport::Frame.parse(buffer)
159
+ end
160
+
161
+ @logger.info("received") { frame } if @logging
162
+
163
+ raise Bunny::ConnectionError, 'No connection to server' if (frame.nil? and !connecting?)
164
+
165
+ # Monitor server activity and discard heartbeats
166
+ @message_in = true
167
+
168
+ case
169
+ when frame.is_a?(Qrack::Transport::Heartbeat)
170
+ next_frame(opts)
171
+ when frame.nil?
172
+ frame
173
+ when ((frame.channel != channel.number) and (frame.channel != 0))
174
+ channel.frame_buffer << frame
175
+ next_frame(opts)
176
+ else
177
+ frame
178
+ end
179
+ end
180
+
181
+ def open_connection
182
+ client_opts = { :platform => 'Ruby', :product => 'Bunny', :information => 'http://github.com/ruby-amqp/bunny', :version => VERSION }
183
+
184
+ send_frame(Qrack::Protocol::Connection::StartOk.new(client_opts, 'AMQPLAIN', {:LOGIN => @user, :PASSWORD => @pass}, 'en_US'))
185
+
186
+ frame = next_frame
187
+ raise Bunny::ProtocolError, "Connection failed - user: #{@user}" if frame.nil?
188
+
189
+ method = frame.payload
190
+
191
+ if method.is_a?(Qrack::Protocol::Connection::Tune)
192
+ send_frame(Qrack::Protocol::Connection::TuneOk.new( :channel_max => @channel_max, :frame_max => @frame_max, :heartbeat => @heartbeat))
193
+ end
194
+
195
+ send_frame(Qrack::Protocol::Connection::Open.new(:virtual_host => @vhost, :capabilities => '', :insist => @insist))
196
+
197
+ case method = next_method
198
+ when Qrack::Protocol::Connection::OpenOk
199
+ :ok
200
+ when Qrack::Protocol::Connection::Redirect
201
+ raise Bunny::ConnectionError, "Cannot connect to the specified server - host: #{@host}, port: #{@port}" if @insist
202
+
203
+ @host, @port = method.host.split(':')
204
+ close_socket
205
+ else
206
+ raise Bunny::ProtocolError, 'Cannot open connection'
207
+ end
208
+ end
209
+
210
+ =begin rdoc
211
+
212
+ === DESCRIPTION:
213
+
214
+ Requests a specific quality of service. The QoS can be specified for the current channel
215
+ or for all channels on the connection. The particular properties and semantics of a QoS
216
+ method always depend on the content class semantics. Though the QoS method could in principle
217
+ apply to both peers, it is currently meaningful only for the server.
218
+
219
+ ==== Options:
220
+
221
+ * <tt>:prefetch_size => size in no. of octets (default = 0)</tt> - The client can request that
222
+ messages be sent in advance so that when the client finishes processing a message, the following
223
+ message is already held locally, rather than needing to be sent down the channel. Prefetching gives
224
+ a performance improvement. This field specifies the prefetch window size in octets. The server
225
+ will send a message in advance if it is equal to or smaller in size than the available prefetch
226
+ size (and also falls into other prefetch limits). May be set to zero, meaning "no specific limit",
227
+ although other prefetch limits may still apply. The prefetch-size is ignored if the no-ack option
228
+ is set.
229
+ * <tt>:prefetch_count => no. messages (default = 1)</tt> - Specifies a prefetch window in terms
230
+ of whole messages. This field may be used in combination with the prefetch-size field; a message
231
+ will only be sent in advance if both prefetch windows (and those at the channel and connection level)
232
+ allow it. The prefetch-count is ignored if the no-ack option is set.
233
+ * <tt>:global => true or false (_default_)</tt> - By default the QoS settings apply to the current channel only. If set to
234
+ true, they are applied to the entire connection.
235
+
236
+ ==== RETURNS:
237
+
238
+ <tt>:qos_ok</tt> if successful.
239
+
240
+ =end
241
+
242
+ def qos(opts = {})
243
+ send_frame(Qrack::Protocol::Basic::Qos.new({ :prefetch_size => 0, :prefetch_count => 1, :global => false }.merge(opts)))
244
+
245
+ method = next_method
246
+
247
+ check_response(method, Qrack::Protocol::Basic::QosOk, "Error specifying Quality of Service")
248
+
249
+ # return confirmation
250
+ :qos_ok
251
+ end
252
+
253
+ =begin rdoc
254
+
255
+ === DESCRIPTION:
256
+
257
+ Declares a queue to the broker/server. If the queue does not exist, a new one is created
258
+ using the arguments passed in. If the queue already exists, a reference to it is created, provided
259
+ that the arguments passed in do not conflict with the existing attributes of the queue. If an error
260
+ occurs a _Bunny_::_ProtocolError_ is raised.
261
+
262
+ ==== OPTIONS:
263
+
264
+ * <tt>:passive => true or false (_default_)</tt> - If set to _true_, the server will not create
265
+ the queue. The client can use this to check whether a queue exists without modifying the server
266
+ state.
267
+ * <tt>:durable => true or false (_default_)</tt> - If set to _true_ when creating a new queue, the
268
+ queue will be marked as durable. Durable queues remain active when a server restarts. Non-durable
269
+ queues (transient queues) are purged if/when a server restarts. Note that durable queues do not
270
+ necessarily hold persistent messages, although it does not make sense to send persistent messages
271
+ to a transient queue.
272
+ * <tt>:exclusive => true or false (_default_)</tt> - If set to _true_, requests an exclusive queue.
273
+ Exclusive queues may only be consumed from by the current connection. Setting the 'exclusive'
274
+ flag always implies 'auto-delete'.
275
+ * <tt>:auto_delete => true or false (_default_)</tt> - If set to _true_, the queue is deleted
276
+ when all consumers have finished using it. Last consumer can be cancelled either explicitly
277
+ or because its channel is closed. If there has never been a consumer on the queue, it is not
278
+ deleted.
279
+ * <tt>:nowait => true or false (_default_)</tt> - Ignored by Bunny, always _false_.
280
+
281
+ ==== RETURNS:
282
+
283
+ Queue
284
+
285
+ =end
286
+
287
+ def queue(name = nil, opts = {})
288
+ if name.is_a?(Hash)
289
+ opts = name
290
+ name = nil
291
+ end
292
+
293
+ # Queue is responsible for placing itself in the list of queues
294
+ queues[name] || Bunny::Queue.new(self, name, opts)
295
+ end
296
+
297
+ =begin rdoc
298
+
299
+ === DESCRIPTION:
300
+
301
+ Asks the broker to redeliver all unacknowledged messages on a specified channel. Zero or
302
+ more messages may be redelivered.
303
+
304
+ ==== Options:
305
+
306
+ * <tt>:requeue => true or false (_default_)</tt> - If set to _false_, the message will be
307
+ redelivered to the original recipient. If set to _true_, the server will attempt to requeue
308
+ the message, potentially then delivering it to an alternative subscriber.
309
+
310
+ =end
311
+
312
+ def recover(opts = {})
313
+ send_frame(Qrack::Protocol::Basic::Recover.new({ :requeue => false }.merge(opts)))
314
+ end
315
+
316
+ def request_access
317
+ send_frame(Qrack::Protocol::Access::Request.new(:realm => '/data', :read => true, :write => true, :active => true, :passive => true))
318
+
319
+ method = next_method
320
+
321
+ check_response(method, Qrack::Protocol::Access::RequestOk, "Access denied")
322
+
323
+ self.ticket = method.ticket
324
+ end
325
+
326
+ def send_frame(*args)
327
+ args.each do |data|
328
+ data.ticket = ticket if ticket and data.respond_to?(:ticket=)
329
+ data = data.to_frame(channel.number) unless data.is_a?(Qrack::Transport::Frame)
330
+ data.channel = channel.number
331
+
332
+ @logger.info("send") { data } if @logging
333
+ write(data.to_s)
334
+
335
+ # Monitor client activity for heartbeat purposes
336
+ @message_out = true
337
+ end
338
+
339
+ nil
340
+ end
341
+
342
+ def send_heartbeat
343
+ # Create a new heartbeat frame
344
+ hb = Qrack::Transport::Heartbeat.new('')
345
+ # Channel 0 must be used
346
+ switch_channel(0) if @channel.number > 0
347
+ # Send the heartbeat to server
348
+ send_frame(hb)
349
+ end
350
+
351
+ =begin rdoc
352
+
353
+ === DESCRIPTION:
354
+
355
+ Opens a communication channel and starts a connection. If an error occurs, a
356
+ _Bunny_::_ProtocolError_ is raised. If successful, _Client_._status_ is set to <tt>:connected</tt>.
357
+
358
+ ==== RETURNS:
359
+
360
+ <tt>:connected</tt> if successful.
361
+
362
+ =end
363
+
364
+ def start_session
365
+ @connecting = true
366
+
367
+ loop do
368
+ # Create/get socket
369
+ socket
370
+
371
+ # Initiate connection
372
+ init_connection
373
+
374
+ # Open connection
375
+ break if open_connection == :ok
376
+ end
377
+
378
+ # Open another channel because channel zero is used for specific purposes
379
+ c = create_channel()
380
+ c.open
381
+
382
+ # Get access ticket
383
+ request_access
384
+
385
+ @connecting = false
386
+
387
+ # return status
388
+ @status = :connected
389
+ end
390
+
391
+ alias start start_session
392
+
393
+ =begin rdoc
394
+
395
+ === DESCRIPTION:
396
+ This method commits all messages published and acknowledged in
397
+ the current transaction. A new transaction starts immediately
398
+ after a commit.
399
+
400
+ ==== RETURNS:
401
+
402
+ <tt>:commit_ok</tt> if successful.
403
+
404
+ =end
405
+
406
+ def tx_commit
407
+ send_frame(Qrack::Protocol::Tx::Commit.new())
408
+
409
+ method = next_method
410
+
411
+ check_response(method, Qrack::Protocol::Tx::CommitOk, "Error commiting transaction")
412
+
413
+ # return confirmation
414
+ :commit_ok
415
+ end
416
+
417
+ =begin rdoc
418
+
419
+ === DESCRIPTION:
420
+ This method abandons all messages published and acknowledged in
421
+ the current transaction. A new transaction starts immediately
422
+ after a rollback.
423
+
424
+ ==== RETURNS:
425
+
426
+ <tt>:rollback_ok</tt> if successful.
427
+
428
+ =end
429
+
430
+ def tx_rollback
431
+ send_frame(Qrack::Protocol::Tx::Rollback.new())
432
+
433
+ method = next_method
434
+
435
+ check_response(method, Qrack::Protocol::Tx::RollbackOk, "Error rolling back transaction")
436
+
437
+ # return confirmation
438
+ :rollback_ok
439
+ end
440
+
441
+ =begin rdoc
442
+
443
+ === DESCRIPTION:
444
+ This method sets the channel to use standard transactions. The
445
+ client must use this method at least once on a channel before
446
+ using the Commit or Rollback methods.
447
+
448
+ ==== RETURNS:
449
+
450
+ <tt>:select_ok</tt> if successful.
451
+
452
+ =end
453
+
454
+ def tx_select
455
+ send_frame(Qrack::Protocol::Tx::Select.new())
456
+
457
+ method = next_method
458
+
459
+ check_response(method, Qrack::Protocol::Tx::SelectOk, "Error initiating transactions for current channel")
460
+
461
+ # return confirmation
462
+ :select_ok
463
+ end
464
+
465
+ private
466
+
467
+ def buffer
468
+ @buffer ||= Qrack::Transport::Buffer.new(self)
469
+ end
470
+
471
+ end
472
+ end