ivanvanderbyl-amqp 0.6.13.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.gitignore +4 -0
  2. data/HISTORY +27 -0
  3. data/README.md +169 -0
  4. data/Rakefile +24 -0
  5. data/TODO +32 -0
  6. data/VERSION +1 -0
  7. data/doc/EXAMPLE_01_PINGPONG +2 -0
  8. data/doc/EXAMPLE_02_CLOCK +2 -0
  9. data/doc/EXAMPLE_03_STOCKS +2 -0
  10. data/doc/EXAMPLE_04_MULTICLOCK +2 -0
  11. data/doc/EXAMPLE_05_ACK +2 -0
  12. data/doc/EXAMPLE_05_POP +2 -0
  13. data/doc/EXAMPLE_06_HASHTABLE +2 -0
  14. data/lib/amqp.rb +110 -0
  15. data/lib/amqp/buffer.rb +270 -0
  16. data/lib/amqp/client.rb +225 -0
  17. data/lib/amqp/frame.rb +66 -0
  18. data/lib/amqp/protocol.rb +161 -0
  19. data/lib/amqp/server.rb +99 -0
  20. data/lib/amqp/spec.rb +832 -0
  21. data/lib/amqp/version.rb +6 -0
  22. data/lib/ext/blankslate.rb +7 -0
  23. data/lib/ext/em.rb +8 -0
  24. data/lib/ext/emfork.rb +69 -0
  25. data/lib/mq.rb +875 -0
  26. data/lib/mq/exchange.rb +351 -0
  27. data/lib/mq/header.rb +33 -0
  28. data/lib/mq/logger.rb +89 -0
  29. data/lib/mq/queue.rb +455 -0
  30. data/lib/mq/rpc.rb +100 -0
  31. data/old/README +30 -0
  32. data/old/Rakefile +12 -0
  33. data/old/amqp-0.8.json +606 -0
  34. data/old/amqp_spec.rb +796 -0
  35. data/old/amqpc.rb +695 -0
  36. data/old/codegen.rb +148 -0
  37. data/protocol/amqp-0.8.json +617 -0
  38. data/protocol/amqp-0.8.xml +3908 -0
  39. data/protocol/codegen.rb +173 -0
  40. data/protocol/doc.txt +281 -0
  41. data/research/api.rb +88 -0
  42. data/research/primes-forked.rb +63 -0
  43. data/research/primes-processes.rb +135 -0
  44. data/research/primes-threaded.rb +49 -0
  45. data/tasks/common.rake +18 -0
  46. data/tasks/doc.rake +14 -0
  47. data/tasks/gem.rake +40 -0
  48. data/tasks/git.rake +34 -0
  49. data/tasks/spec.rake +15 -0
  50. data/tasks/version.rake +71 -0
  51. metadata +158 -0
@@ -0,0 +1,225 @@
1
+ require File.expand_path('../frame', __FILE__)
2
+
3
+ module AMQP
4
+ class Error < StandardError; end
5
+
6
+ module BasicClient
7
+ def process_frame frame
8
+ if mq = channels[frame.channel]
9
+ mq.process_frame(frame)
10
+ return
11
+ end
12
+
13
+ case frame
14
+ when Frame::Method
15
+ case method = frame.payload
16
+ when Protocol::Connection::Start
17
+ send Protocol::Connection::StartOk.new({:platform => 'Ruby/EventMachine',
18
+ :product => 'AMQP',
19
+ :information => 'http://github.com/tmm1/amqp',
20
+ :version => VERSION},
21
+ 'AMQPLAIN',
22
+ {:LOGIN => @settings[:user],
23
+ :PASSWORD => @settings[:pass]},
24
+ 'en_US')
25
+
26
+ when Protocol::Connection::Tune
27
+ send Protocol::Connection::TuneOk.new(:channel_max => 0,
28
+ :frame_max => 131072,
29
+ :heartbeat => @settings[:heartbeat] || 0)
30
+
31
+ send Protocol::Connection::Open.new(:virtual_host => @settings[:vhost],
32
+ :capabilities => '',
33
+ :insist => @settings[:insist])
34
+
35
+ when Protocol::Connection::OpenOk
36
+ succeed(self)
37
+
38
+ when Protocol::Connection::Close
39
+ # raise Error, "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]}"
40
+ STDERR.puts "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]}"
41
+
42
+ when Protocol::Connection::CloseOk
43
+ @on_disconnect.call if @on_disconnect
44
+ end
45
+
46
+ when Frame::Heartbeat
47
+ @last_server_heartbeat = Time.now
48
+ end
49
+ end
50
+ end
51
+
52
+ def self.client
53
+ @client ||= BasicClient
54
+ end
55
+
56
+ def self.client= mod
57
+ mod.__send__ :include, AMQP
58
+ @client = mod
59
+ end
60
+
61
+ module Client
62
+ include EM::Deferrable
63
+
64
+ def initialize opts = {}
65
+ @settings = opts
66
+ extend AMQP.client
67
+
68
+ @on_disconnect ||= proc{ raise Error, "Could not connect to server #{opts[:host]}:#{opts[:port]}" }
69
+
70
+ timeout @settings[:timeout] if @settings[:timeout]
71
+ errback{ @on_disconnect.call } unless @reconnecting
72
+
73
+ @connected = false
74
+ end
75
+
76
+ def connection_completed
77
+ start_tls if @settings[:ssl]
78
+ log 'connected'
79
+ # @on_disconnect = proc{ raise Error, 'Disconnected from server' }
80
+ unless @closing
81
+ @on_disconnect = method(:disconnected)
82
+ @reconnecting = false
83
+ end
84
+
85
+ @connected = true
86
+ @connection_status.call(:connected) if @connection_status
87
+
88
+ @buf = Buffer.new
89
+ send_data HEADER
90
+ send_data [1, 1, VERSION_MAJOR, VERSION_MINOR].pack('C4')
91
+
92
+ if heartbeat = @settings[:heartbeat]
93
+ init_heartbeat if (@settings[:heartbeat] = heartbeat.to_i) > 0
94
+ end
95
+ end
96
+
97
+ def init_heartbeat
98
+ @last_server_heartbeat = Time.now
99
+
100
+ @timer ||= EM::PeriodicTimer.new(@settings[:heartbeat]) do
101
+ if connected?
102
+ if @last_server_heartbeat < (Time.now - (@settings[:heartbeat] * 2))
103
+ log "Reconnecting due to missing server heartbeats"
104
+ reconnect(true)
105
+ else
106
+ send AMQP::Frame::Heartbeat.new
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ def connected?
113
+ @connected
114
+ end
115
+
116
+ def unbind
117
+ log 'disconnected'
118
+ @connected = false
119
+ EM.next_tick{ @on_disconnect.call }
120
+ end
121
+
122
+ def add_channel mq
123
+ (@_channel_mutex ||= Mutex.new).synchronize do
124
+ channels[ key = (channels.keys.max || 0) + 1 ] = mq
125
+ key
126
+ end
127
+ end
128
+
129
+ def channels
130
+ @channels ||= {}
131
+ end
132
+
133
+ def receive_data data
134
+ # log 'receive_data', data
135
+ @buf << data
136
+
137
+ while frame = Frame.parse(@buf)
138
+ log 'receive', frame
139
+ process_frame frame
140
+ end
141
+ end
142
+
143
+ def process_frame frame
144
+ # this is a stub meant to be
145
+ # replaced by the module passed into initialize
146
+ end
147
+
148
+ def send data, opts = {}
149
+ channel = opts[:channel] ||= 0
150
+ data = data.to_frame(channel) unless data.is_a? Frame
151
+ data.channel = channel
152
+
153
+ log 'send', data
154
+ send_data data.to_s
155
+ end
156
+
157
+ def close &on_disconnect
158
+ if on_disconnect
159
+ @closing = true
160
+ @on_disconnect = proc{
161
+ on_disconnect.call
162
+ @closing = false
163
+ }
164
+ end
165
+
166
+ callback{ |c|
167
+ if c.channels.any?
168
+ c.channels.each do |ch, mq|
169
+ mq.close
170
+ end
171
+ else
172
+ send Protocol::Connection::Close.new(:reply_code => 200,
173
+ :reply_text => 'Goodbye',
174
+ :class_id => 0,
175
+ :method_id => 0)
176
+ end
177
+ }
178
+ end
179
+
180
+ def reconnect force = false
181
+ if @reconnecting and not force
182
+ # wait 1 second after first reconnect attempt, in between each subsequent attempt
183
+ EM.add_timer(1){ reconnect(true) }
184
+ return
185
+ end
186
+
187
+ unless @reconnecting
188
+ @reconnecting = true
189
+
190
+ @deferred_status = nil
191
+ initialize(@settings)
192
+
193
+ mqs = @channels
194
+ @channels = {}
195
+ mqs.each{ |_,mq| mq.reset } if mqs
196
+ end
197
+
198
+ log 'reconnecting'
199
+ EM.reconnect @settings[:host], @settings[:port], self
200
+ end
201
+
202
+ def self.connect opts = {}
203
+ opts = AMQP.settings.merge(opts)
204
+ EM.connect opts[:host], opts[:port], self, opts
205
+ end
206
+
207
+ def connection_status &blk
208
+ @connection_status = blk
209
+ end
210
+
211
+ private
212
+
213
+ def disconnected
214
+ @connection_status.call(:disconnected) if @connection_status
215
+ reconnect
216
+ end
217
+
218
+ def log *args
219
+ return unless @settings[:logging] or AMQP.logging
220
+ require 'pp'
221
+ pp args
222
+ puts
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,66 @@
1
+ require File.expand_path('../spec', __FILE__)
2
+ require File.expand_path('../buffer', __FILE__)
3
+ require File.expand_path('../protocol', __FILE__)
4
+
5
+ module AMQP
6
+ class Frame #:nodoc: all
7
+ def initialize payload = nil, channel = 0
8
+ @channel, @payload = channel, payload
9
+ end
10
+ attr_accessor :channel, :payload
11
+
12
+ def id
13
+ self.class::ID
14
+ end
15
+
16
+ def to_binary
17
+ buf = Buffer.new
18
+ buf.write :octet, id
19
+ buf.write :short, channel
20
+ buf.write :longstr, payload
21
+ buf.write :octet, FOOTER
22
+ buf.rewind
23
+ buf
24
+ end
25
+
26
+ def to_s
27
+ to_binary.to_s
28
+ end
29
+
30
+ def == frame
31
+ [ :id, :channel, :payload ].inject(true) do |eql, field|
32
+ eql and __send__(field) == frame.__send__(field)
33
+ end
34
+ end
35
+
36
+ class Invalid < StandardError; end
37
+
38
+ class Method
39
+ def initialize payload = nil, channel = 0
40
+ super
41
+ unless @payload.is_a? Protocol::Class::Method or @payload.nil?
42
+ @payload = Protocol.parse(@payload)
43
+ end
44
+ end
45
+ end
46
+
47
+ class Header
48
+ def initialize payload = nil, channel = 0
49
+ super
50
+ unless @payload.is_a? Protocol::Header or @payload.nil?
51
+ @payload = Protocol::Header.new(@payload)
52
+ end
53
+ end
54
+ end
55
+
56
+ class Body; end
57
+
58
+ def self.parse buf
59
+ buf = Buffer.new(buf) unless buf.is_a? Buffer
60
+ buf.extract do
61
+ id, channel, payload, footer = buf.read(:octet, :short, :longstr, :octet)
62
+ Frame.types[id].new(payload, channel) if footer == FOOTER
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,161 @@
1
+ require File.expand_path('../spec', __FILE__)
2
+ require File.expand_path('../buffer', __FILE__)
3
+
4
+ module AMQP
5
+ module Protocol
6
+ #:stopdoc:
7
+ class Class::Method
8
+ def initialize *args
9
+ opts = args.pop if args.last.is_a? Hash
10
+ opts ||= {}
11
+
12
+ @debug = 1 # XXX hack, p(obj) == '' if no instance vars are set
13
+
14
+ if args.size == 1 and args.first.is_a? Buffer
15
+ buf = args.shift
16
+ else
17
+ buf = nil
18
+ end
19
+
20
+ self.class.arguments.each do |type, name|
21
+ val = buf ? buf.read(type) :
22
+ args.shift || opts[name] || opts[name.to_s]
23
+ instance_variable_set("@#{name}", val)
24
+ end
25
+ end
26
+
27
+ def arguments
28
+ self.class.arguments.inject({}) do |hash, (type, name)|
29
+ hash.update name => instance_variable_get("@#{name}")
30
+ end
31
+ end
32
+
33
+ def to_binary
34
+ buf = Buffer.new
35
+ buf.write :short, self.class.section.id
36
+ buf.write :short, self.class.id
37
+
38
+ bits = []
39
+
40
+ self.class.arguments.each do |type, name|
41
+ val = instance_variable_get("@#{name}")
42
+ if type == :bit
43
+ bits << (val || false)
44
+ else
45
+ unless bits.empty?
46
+ buf.write :bit, bits
47
+ bits = []
48
+ end
49
+ buf.write type, val
50
+ end
51
+ end
52
+
53
+ buf.write :bit, bits unless bits.empty?
54
+ buf.rewind
55
+
56
+ buf
57
+ end
58
+
59
+ def to_s
60
+ to_binary.to_s
61
+ end
62
+
63
+ def to_frame channel = 0
64
+ Frame::Method.new(self, channel)
65
+ end
66
+ end
67
+
68
+ #:startdoc:
69
+ #
70
+ # Contains a properties hash that holds some potentially interesting
71
+ # information.
72
+ # * :delivery_mode
73
+ # 1 equals transient.
74
+ # 2 equals persistent. Unconsumed persistent messages will survive
75
+ # a server restart when they are stored in a durable queue.
76
+ # * :redelivered
77
+ # True or False
78
+ # * :routing_key
79
+ # The routing string used for matching this message to this queue.
80
+ # * :priority
81
+ # An integer in the range of 0 to 9 inclusive.
82
+ # * :content_type
83
+ # Always "application/octet-stream" (byte stream)
84
+ # * :exchange
85
+ # The source exchange which published this message.
86
+ # * :message_count
87
+ # The number of unconsumed messages contained in the queue.
88
+ # * :delivery_tag
89
+ # A monotonically increasing integer. This number should not be trusted
90
+ # as a sequence number. There is no guarantee it won't get reset.
91
+ class Header
92
+ def initialize *args
93
+ opts = args.pop if args.last.is_a? Hash
94
+ opts ||= {}
95
+
96
+ first = args.shift
97
+
98
+ if first.is_a? ::Class and first.ancestors.include? Protocol::Class
99
+ @klass = first
100
+ @size = args.shift || 0
101
+ @weight = args.shift || 0
102
+ @properties = opts
103
+
104
+ elsif first.is_a? Buffer or first.is_a? String
105
+ buf = first
106
+ buf = Buffer.new(buf) unless buf.is_a? Buffer
107
+
108
+ @klass = Protocol.classes[buf.read(:short)]
109
+ @weight = buf.read(:short)
110
+ @size = buf.read(:longlong)
111
+
112
+ props = buf.read(:properties, *klass.properties.map{|type,_| type })
113
+ @properties = Hash[*klass.properties.map{|_,name| name }.zip(props).reject{|k,v| v.nil? }.flatten]
114
+
115
+ else
116
+ raise ArgumentError, 'Invalid argument'
117
+ end
118
+
119
+ end
120
+ attr_accessor :klass, :size, :weight, :properties
121
+
122
+ def to_binary
123
+ buf = Buffer.new
124
+ buf.write :short, klass.id
125
+ buf.write :short, weight # XXX rabbitmq only supports weight == 0
126
+ buf.write :longlong, size
127
+ buf.write :properties, (klass.properties.map do |type, name|
128
+ [ type, properties[name] || properties[name.to_s] ]
129
+ end)
130
+ buf.rewind
131
+ buf
132
+ end
133
+
134
+ def to_s
135
+ to_binary.to_s
136
+ end
137
+
138
+ def to_frame channel = 0
139
+ Frame::Header.new(self, channel)
140
+ end
141
+
142
+ def == header
143
+ [ :klass, :size, :weight, :properties ].inject(true) do |eql, field|
144
+ eql and __send__(field) == header.__send__(field)
145
+ end
146
+ end
147
+
148
+ def method_missing meth, *args, &blk
149
+ @properties.has_key?(meth) || @klass.properties.find{|_,name| name == meth } ? @properties[meth] :
150
+ super
151
+ end
152
+ end
153
+
154
+ def self.parse buf
155
+ buf = Buffer.new(buf) unless buf.is_a? Buffer
156
+ class_id, method_id = buf.read(:short, :short)
157
+ classes[class_id].methods[method_id].new(buf)
158
+ end
159
+ #:stopdoc:
160
+ end
161
+ end