rabbitmq 0.2.5 → 1.0.0.pre.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +67 -2
- data/lib/rabbitmq.rb +1 -1
- data/lib/rabbitmq/channel.rb +117 -81
- data/lib/rabbitmq/client.rb +332 -0
- data/lib/rabbitmq/{connection/transport.rb → client/connection.rb} +32 -11
- data/lib/rabbitmq/ffi.rb +486 -483
- data/lib/rabbitmq/util.rb +6 -3
- metadata +6 -7
- data/lib/rabbitmq/connection.rb +0 -382
- data/lib/rabbitmq/connection/channel_manager.rb +0 -61
- data/lib/rabbitmq/connection/event_manager.rb +0 -55
data/lib/rabbitmq/util.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
|
2
2
|
module RabbitMQ
|
3
|
-
|
4
|
-
|
3
|
+
|
4
|
+
# Helper functions for this library.
|
5
|
+
# @api private
|
6
|
+
module Util
|
7
|
+
module_function
|
5
8
|
|
6
9
|
def const_name(lowercase_name)
|
7
10
|
lowercase_name.to_s.gsub(/((?:\A\w)|(?:_\w))/) { |x| x[-1].upcase }
|
@@ -47,6 +50,6 @@ module RabbitMQ
|
|
47
50
|
|
48
51
|
result.merge(overrides)
|
49
52
|
end
|
50
|
-
|
51
53
|
end
|
54
|
+
|
52
55
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rabbitmq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0.pre.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe McIlvain
|
@@ -126,10 +126,8 @@ files:
|
|
126
126
|
- ext/rabbitmq/Rakefile
|
127
127
|
- lib/rabbitmq.rb
|
128
128
|
- lib/rabbitmq/channel.rb
|
129
|
-
- lib/rabbitmq/
|
130
|
-
- lib/rabbitmq/connection
|
131
|
-
- lib/rabbitmq/connection/event_manager.rb
|
132
|
-
- lib/rabbitmq/connection/transport.rb
|
129
|
+
- lib/rabbitmq/client.rb
|
130
|
+
- lib/rabbitmq/client/connection.rb
|
133
131
|
- lib/rabbitmq/ffi.rb
|
134
132
|
- lib/rabbitmq/ffi/error.rb
|
135
133
|
- lib/rabbitmq/ffi/ext.rb
|
@@ -224,9 +222,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
224
222
|
version: '0'
|
225
223
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
226
224
|
requirements:
|
227
|
-
- - "
|
225
|
+
- - ">"
|
228
226
|
- !ruby/object:Gem::Version
|
229
|
-
version:
|
227
|
+
version: 1.3.1
|
230
228
|
requirements: []
|
231
229
|
rubyforge_project:
|
232
230
|
rubygems_version: 2.2.2
|
@@ -234,3 +232,4 @@ signing_key:
|
|
234
232
|
specification_version: 4
|
235
233
|
summary: rabbitmq
|
236
234
|
test_files: []
|
235
|
+
has_rdoc:
|
data/lib/rabbitmq/connection.rb
DELETED
@@ -1,382 +0,0 @@
|
|
1
|
-
|
2
|
-
require 'socket'
|
3
|
-
|
4
|
-
module RabbitMQ
|
5
|
-
class Connection
|
6
|
-
def initialize *args
|
7
|
-
@ptr = FFI.amqp_new_connection
|
8
|
-
@info = Util.connection_info(*args)
|
9
|
-
|
10
|
-
@open_channels = {}
|
11
|
-
@released_channels = {}
|
12
|
-
@event_handlers = Hash.new { |h,k| h[k] = {} }
|
13
|
-
@incoming_events = Hash.new { |h,k| h[k] = {} }
|
14
|
-
|
15
|
-
@frame = FFI::Frame.new
|
16
|
-
|
17
|
-
create_socket!
|
18
|
-
|
19
|
-
@finalizer = self.class.send :create_finalizer_for, @ptr
|
20
|
-
ObjectSpace.define_finalizer self, @finalizer
|
21
|
-
end
|
22
|
-
|
23
|
-
def destroy
|
24
|
-
if @finalizer
|
25
|
-
@finalizer.call
|
26
|
-
ObjectSpace.undefine_finalizer self
|
27
|
-
end
|
28
|
-
@ptr = @socket = @finalizer = nil
|
29
|
-
end
|
30
|
-
|
31
|
-
class DestroyedError < RuntimeError; end
|
32
|
-
|
33
|
-
# @private
|
34
|
-
def self.create_finalizer_for(ptr)
|
35
|
-
Proc.new do
|
36
|
-
FFI.amqp_connection_close(ptr, 200)
|
37
|
-
FFI.amqp_destroy_connection(ptr)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def user; @info[:user]; end
|
42
|
-
def password; @info[:password]; end
|
43
|
-
def host; @info[:host]; end
|
44
|
-
def vhost; @info[:vhost]; end
|
45
|
-
def port; @info[:port]; end
|
46
|
-
def ssl?; @info[:ssl]; end
|
47
|
-
|
48
|
-
def protocol_timeout
|
49
|
-
@protocol_timeout ||= 30 # seconds
|
50
|
-
end
|
51
|
-
attr_writer :protocol_timeout
|
52
|
-
|
53
|
-
def max_channels
|
54
|
-
@max_channels ||= FFI::CHANNEL_MAX_ID
|
55
|
-
end
|
56
|
-
attr_writer :max_channels
|
57
|
-
|
58
|
-
def max_frame_size
|
59
|
-
@max_frame_size ||= 131072
|
60
|
-
end
|
61
|
-
attr_writer :max_frame_size
|
62
|
-
|
63
|
-
def heartbeat_interval; 0; end # not fully implemented in librabbitmq
|
64
|
-
|
65
|
-
def start
|
66
|
-
close # Close if already open
|
67
|
-
connect_socket!
|
68
|
-
login!
|
69
|
-
|
70
|
-
self
|
71
|
-
end
|
72
|
-
|
73
|
-
def close
|
74
|
-
raise DestroyedError unless @ptr
|
75
|
-
FFI.amqp_connection_close(@ptr, 200)
|
76
|
-
|
77
|
-
release_all_channels
|
78
|
-
|
79
|
-
self
|
80
|
-
end
|
81
|
-
|
82
|
-
# Send a request on the given channel with the given type and properties.
|
83
|
-
#
|
84
|
-
# @param channel_id [Integer] The channel number to send on.
|
85
|
-
# @param method [Symbol] The type of protocol method to send.
|
86
|
-
# @param properties [Hash] The properties to apply to the method.
|
87
|
-
# @raise [RabbitMQ::FFI::Error] if a library exception occurs.
|
88
|
-
#
|
89
|
-
def send_request(channel_id, method, properties={})
|
90
|
-
Util.error_check :"sending a request",
|
91
|
-
send_request_internal(Integer(channel_id), method.to_sym, properties)
|
92
|
-
|
93
|
-
nil
|
94
|
-
end
|
95
|
-
|
96
|
-
# Wait for a specific response on the given channel of the given type
|
97
|
-
# and return the event data for the response when it is received.
|
98
|
-
# Any other events received will be processed or stored internally.
|
99
|
-
#
|
100
|
-
# @param channel_id [Integer] The channel number to watch for.
|
101
|
-
# @param method [Symbol,Array<Symbol>] The protocol method(s) to watch for.
|
102
|
-
# @param timeout [Float] The maximum time to wait for a response in seconds;
|
103
|
-
# uses the value of {#protocol_timeout} by default.
|
104
|
-
# @raise [RabbitMQ::ServerError] if any error event is received.
|
105
|
-
# @raise [RabbitMQ::FFI::Error::Timeout] if no event is received.
|
106
|
-
# @raise [RabbitMQ::FFI::Error] if a library exception occurs.
|
107
|
-
# @return [Hash] the response data received.
|
108
|
-
#
|
109
|
-
def fetch_response(channel_id, method, timeout=protocol_timeout)
|
110
|
-
methods = Array(method).map(&:to_sym)
|
111
|
-
fetch_response_internal(Integer(channel_id), methods, Float(timeout))
|
112
|
-
end
|
113
|
-
|
114
|
-
# Register a handler for events on the given channel of the given type.
|
115
|
-
#
|
116
|
-
# @param channel_id [Integer] The channel number to watch for.
|
117
|
-
# @param method [Symbol] The type of protocol method to watch for.
|
118
|
-
# @param callable [#call,nil] The callable handler if no block is given.
|
119
|
-
# @param block [Proc,nil] The handler block to register.
|
120
|
-
# @return [Proc,#call] The given block or callable.
|
121
|
-
# @yieldparam event [Hash] The event passed to the handler.
|
122
|
-
#
|
123
|
-
def on_event(channel_id, method, callable=nil, &block)
|
124
|
-
handler = block || callable
|
125
|
-
raise ArgumentError, "expected block or callable as event handler" \
|
126
|
-
unless handler.respond_to?(:call)
|
127
|
-
|
128
|
-
@event_handlers[Integer(channel_id)][method.to_sym] = handler
|
129
|
-
handler
|
130
|
-
end
|
131
|
-
|
132
|
-
# Fetch and handle events in a loop that blocks the calling thread.
|
133
|
-
# The loop will continue until the {#break!} method is called from within
|
134
|
-
# an event handler, or until the given timeout duration has elapsed.
|
135
|
-
#
|
136
|
-
# @param timeout [Float] the maximum time to run the loop, in seconds;
|
137
|
-
# if none is given, the loop will block indefinitely or until {#break!}.
|
138
|
-
#
|
139
|
-
def run_loop!(timeout=nil)
|
140
|
-
timeout = Float(timeout) if timeout
|
141
|
-
@breaking = false
|
142
|
-
fetch_events(timeout)
|
143
|
-
nil
|
144
|
-
end
|
145
|
-
|
146
|
-
# Stop iterating from within an execution of the {#run_loop!} method.
|
147
|
-
# Call this method only from within an event handler.
|
148
|
-
# It will take effect only after the handler finishes running.
|
149
|
-
#
|
150
|
-
# @return [nil]
|
151
|
-
#
|
152
|
-
def break!
|
153
|
-
@breaking = true
|
154
|
-
nil
|
155
|
-
end
|
156
|
-
|
157
|
-
def channel(id=nil)
|
158
|
-
Channel.new(self, allocate_channel(id), pre_allocated: true)
|
159
|
-
end
|
160
|
-
|
161
|
-
private def ptr
|
162
|
-
raise DestroyedError unless @ptr
|
163
|
-
@ptr
|
164
|
-
end
|
165
|
-
|
166
|
-
private def create_socket!
|
167
|
-
raise DestroyedError unless @ptr
|
168
|
-
|
169
|
-
@socket = FFI.amqp_tcp_socket_new(@ptr)
|
170
|
-
Util.null_check :"creating a socket", @socket
|
171
|
-
end
|
172
|
-
|
173
|
-
private def connect_socket!
|
174
|
-
raise DestroyedError unless @ptr
|
175
|
-
raise NotImplementedError if ssl?
|
176
|
-
|
177
|
-
create_socket!
|
178
|
-
Util.error_check :"opening a socket",
|
179
|
-
FFI.amqp_socket_open(@socket, host, port)
|
180
|
-
|
181
|
-
@ruby_socket = Socket.for_fd(FFI.amqp_get_sockfd(@ptr))
|
182
|
-
@ruby_socket.autoclose = false
|
183
|
-
end
|
184
|
-
|
185
|
-
private def login!
|
186
|
-
raise DestroyedError unless @ptr
|
187
|
-
|
188
|
-
res = FFI.amqp_login(@ptr, vhost, max_channels, max_frame_size,
|
189
|
-
heartbeat_interval, :plain, :string, user, :string, password)
|
190
|
-
|
191
|
-
case res[:reply_type]
|
192
|
-
when :library_exception; Util.error_check :"logging in", res[:library_error]
|
193
|
-
when :server_exception; raise NotImplementedError
|
194
|
-
end
|
195
|
-
|
196
|
-
@server_properties = FFI::Table.new(FFI.amqp_get_server_properties(@ptr)).to_h
|
197
|
-
end
|
198
|
-
|
199
|
-
private def open_channel(id)
|
200
|
-
Util.error_check :"opening a new channel",
|
201
|
-
send_request_internal(id, :channel_open)
|
202
|
-
|
203
|
-
fetch_response(id, :channel_open_ok)
|
204
|
-
end
|
205
|
-
|
206
|
-
private def reopen_channel(id)
|
207
|
-
Util.error_check :"acknowledging server-initated channel closure",
|
208
|
-
send_request_internal(id, :channel_close_ok)
|
209
|
-
|
210
|
-
Util.error_check :"reopening channel after server-initated closure",
|
211
|
-
send_request_internal(id, :channel_open)
|
212
|
-
|
213
|
-
fetch_response(id, :channel_open_ok)
|
214
|
-
end
|
215
|
-
|
216
|
-
private def allocate_channel(id=nil)
|
217
|
-
if id
|
218
|
-
raise ArgumentError, "channel #{id} is already in use" if @open_channels[id]
|
219
|
-
elsif @released_channels.empty?
|
220
|
-
id = (@open_channels.keys.sort.last || 0) + 1
|
221
|
-
else
|
222
|
-
id = @released_channels.keys.first
|
223
|
-
end
|
224
|
-
raise ArgumentError, "channel #{id} is too high" if id > max_channels
|
225
|
-
|
226
|
-
already_open = @released_channels.delete(id)
|
227
|
-
open_channel(id) unless already_open
|
228
|
-
|
229
|
-
@open_channels[id] = true
|
230
|
-
@event_handlers[id] ||= {}
|
231
|
-
|
232
|
-
id
|
233
|
-
end
|
234
|
-
|
235
|
-
private def release_channel(id)
|
236
|
-
@open_channels.delete(id)
|
237
|
-
@event_handlers.delete(id)
|
238
|
-
@released_channels[id] = true
|
239
|
-
end
|
240
|
-
|
241
|
-
private def release_all_channels
|
242
|
-
@open_channels.clear
|
243
|
-
@event_handlers.clear
|
244
|
-
@released_channels.clear
|
245
|
-
end
|
246
|
-
|
247
|
-
# Block until there is readable data on the internal ruby socket,
|
248
|
-
# returning true if there is readable data, or false if time expired.
|
249
|
-
private def select(timeout=0, start=Time.now)
|
250
|
-
if timeout
|
251
|
-
timeout = timeout - (start-Time.now)
|
252
|
-
timeout = 0 if timeout < 0
|
253
|
-
end
|
254
|
-
|
255
|
-
IO.select([@ruby_socket], [], [], timeout) ? true : false
|
256
|
-
end
|
257
|
-
|
258
|
-
# Return the next available frame, or nil if time expired.
|
259
|
-
private def fetch_next_frame(timeout=0, start=Time.now)
|
260
|
-
frame = @frame
|
261
|
-
|
262
|
-
# Try fetching the next frame without a blocking call.
|
263
|
-
status = FFI.amqp_simple_wait_frame_noblock(@ptr, frame, FFI::Timeval.zero)
|
264
|
-
case status
|
265
|
-
when :ok; return frame
|
266
|
-
when :timeout; # do nothing and proceed to waiting on select below
|
267
|
-
else Util.error_check :"fetching the next frame", status
|
268
|
-
end
|
269
|
-
|
270
|
-
# Otherwise, wait for the socket to be readable and try fetching again.
|
271
|
-
return nil unless select(timeout, start)
|
272
|
-
Util.error_check :"fetching the next frame",
|
273
|
-
FFI.amqp_simple_wait_frame(@ptr, frame)
|
274
|
-
|
275
|
-
frame
|
276
|
-
end
|
277
|
-
|
278
|
-
# Fetch the next one or more frames to form the next discrete event,
|
279
|
-
# returning the event as a Hash, or nil if time expired.
|
280
|
-
private def fetch_next_event(timeout=0, start=Time.now)
|
281
|
-
frame = fetch_next_frame(timeout, start)
|
282
|
-
return unless frame
|
283
|
-
event = frame.as_method_to_h(false)
|
284
|
-
return event unless FFI::Method.has_content?(event.fetch(:method))
|
285
|
-
|
286
|
-
frame = fetch_next_frame(timeout, start)
|
287
|
-
return unless frame
|
288
|
-
event.merge!(frame.as_header_to_h)
|
289
|
-
|
290
|
-
body = ""
|
291
|
-
while body.size < event.fetch(:body_size)
|
292
|
-
frame = fetch_next_frame(timeout, start)
|
293
|
-
return unless frame
|
294
|
-
body.concat frame.as_body_to_s
|
295
|
-
end
|
296
|
-
|
297
|
-
event[:body] = body
|
298
|
-
event
|
299
|
-
end
|
300
|
-
|
301
|
-
# Execute the handler for this type of event, if any
|
302
|
-
private def handle_incoming_event(event)
|
303
|
-
if (handlers = @event_handlers[event.fetch(:channel)])
|
304
|
-
if (handler = (handlers[event.fetch(:method)]))
|
305
|
-
handler.call(event)
|
306
|
-
end
|
307
|
-
end
|
308
|
-
end
|
309
|
-
|
310
|
-
# Store the event in short-term storage for retrieval by fetch_response.
|
311
|
-
# If another event is received with the same method name, it will
|
312
|
-
# overwrite this one - fetch_response gets the latest or next by method.
|
313
|
-
# Raises an exception if the incoming event is an error condition.
|
314
|
-
private def store_incoming_event(event)
|
315
|
-
method = event.fetch(:method)
|
316
|
-
|
317
|
-
case method
|
318
|
-
when :channel_close
|
319
|
-
raise_if_server_error!(event)
|
320
|
-
when :connection_close
|
321
|
-
raise_if_server_error!(event)
|
322
|
-
else
|
323
|
-
@incoming_events[event.fetch(:channel)][method] = event
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
# Raise an exception if the incoming event is an error condition.
|
328
|
-
# Also takes action to reopen the channel or close the connection.
|
329
|
-
private def raise_if_server_error!(event)
|
330
|
-
if (exc = ServerError.from(event))
|
331
|
-
if exc.is_a?(ServerError::ChannelError)
|
332
|
-
reopen_channel(event.fetch(:channel)) # recover by reopening the channel
|
333
|
-
elsif exc.is_a?(ServerError::ConnectionError)
|
334
|
-
close # can't recover here - close and let the user recover manually
|
335
|
-
end
|
336
|
-
raise exc
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
private def fetch_events(timeout=protocol_timeout, start=Time.now)
|
341
|
-
raise DestroyedError unless @ptr
|
342
|
-
|
343
|
-
FFI.amqp_maybe_release_buffers(@ptr)
|
344
|
-
|
345
|
-
while (event = fetch_next_event(timeout, start))
|
346
|
-
handle_incoming_event(event)
|
347
|
-
store_incoming_event(event)
|
348
|
-
break if @breaking
|
349
|
-
end
|
350
|
-
end
|
351
|
-
|
352
|
-
private def fetch_response_internal(channel, methods, timeout=protocol_timeout, start=Time.now)
|
353
|
-
raise DestroyedError unless @ptr
|
354
|
-
|
355
|
-
methods.each { |method|
|
356
|
-
found = @incoming_events[channel].delete(method)
|
357
|
-
return found if found
|
358
|
-
}
|
359
|
-
|
360
|
-
FFI.amqp_maybe_release_buffers_on_channel(@ptr, channel)
|
361
|
-
|
362
|
-
while (event = fetch_next_event(timeout, start))
|
363
|
-
handle_incoming_event(event)
|
364
|
-
return event if channel == event.fetch(:channel) \
|
365
|
-
&& methods.include?(event.fetch(:method))
|
366
|
-
store_incoming_event(event)
|
367
|
-
end
|
368
|
-
|
369
|
-
raise FFI::Error::Timeout, "waiting for response"
|
370
|
-
end
|
371
|
-
|
372
|
-
private def send_request_internal(channel_id, method, properties={})
|
373
|
-
raise DestroyedError unless @ptr
|
374
|
-
|
375
|
-
req = FFI::Method.lookup_class(method).new.apply(properties)
|
376
|
-
status = FFI.amqp_send_method(@ptr, channel_id, method, req.pointer)
|
377
|
-
|
378
|
-
req.free!
|
379
|
-
status
|
380
|
-
end
|
381
|
-
end
|
382
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
|
2
|
-
module RabbitMQ
|
3
|
-
class Connection
|
4
|
-
|
5
|
-
class ChannelManager
|
6
|
-
def initialize(connection)
|
7
|
-
@connection = connection
|
8
|
-
@open_channels = {}
|
9
|
-
@released_channels = {}
|
10
|
-
end
|
11
|
-
|
12
|
-
def allocate_channel(id=nil)
|
13
|
-
if id
|
14
|
-
raise ArgumentError, "channel #{id} is already in use" if @open_channels[id]
|
15
|
-
elsif @released_channels.empty?
|
16
|
-
id = (@open_channels.keys.sort.last || 0) + 1
|
17
|
-
else
|
18
|
-
id = @released_channels.keys.first
|
19
|
-
end
|
20
|
-
raise ArgumentError, "channel #{id} is too high" if id > @connection.max_channels
|
21
|
-
|
22
|
-
already_open = @released_channels.delete(id)
|
23
|
-
request_channel_open(id) unless already_open
|
24
|
-
|
25
|
-
@open_channels[id] = true
|
26
|
-
@event_handlers[id] ||= {}
|
27
|
-
|
28
|
-
id
|
29
|
-
end
|
30
|
-
|
31
|
-
def release_channel(id)
|
32
|
-
@open_channels.delete(id)
|
33
|
-
@released_channels[id] = true
|
34
|
-
end
|
35
|
-
|
36
|
-
def release_all_channels
|
37
|
-
@open_channels.clear
|
38
|
-
@released_channels.clear
|
39
|
-
end
|
40
|
-
|
41
|
-
def request_channel_open(id)
|
42
|
-
Util.error_check :"opening a new channel",
|
43
|
-
@connection.send_request_internal(id, :channel_open)
|
44
|
-
|
45
|
-
@connection.fetch_response(id, :channel_open_ok)
|
46
|
-
end
|
47
|
-
|
48
|
-
def request_channel_reopen(id)
|
49
|
-
Util.error_check :"acknowledging server-initated channel closure",
|
50
|
-
@connection.send_request_internal(id, :channel_close_ok)
|
51
|
-
|
52
|
-
Util.error_check :"reopening channel after server-initated closure",
|
53
|
-
@connection.send_request_internal(id, :channel_open)
|
54
|
-
|
55
|
-
@connection.fetch_response(id, :channel_open_ok)
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
end
|