amq-client 0.5.0
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.
- data/.gitignore +8 -0
- data/.gitmodules +9 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/.yardopts +1 -0
- data/CONTRIBUTORS +3 -0
- data/Gemfile +27 -0
- data/LICENSE +20 -0
- data/README.textile +61 -0
- data/amq-client.gemspec +34 -0
- data/bin/jenkins.sh +23 -0
- data/bin/set_test_suite_realms_up.sh +24 -0
- data/examples/coolio_adapter/basic_consume.rb +49 -0
- data/examples/coolio_adapter/basic_consume_with_acknowledgements.rb +43 -0
- data/examples/coolio_adapter/basic_consume_with_rejections.rb +43 -0
- data/examples/coolio_adapter/basic_publish.rb +35 -0
- data/examples/coolio_adapter/channel_close.rb +24 -0
- data/examples/coolio_adapter/example_helper.rb +39 -0
- data/examples/coolio_adapter/exchange_declare.rb +28 -0
- data/examples/coolio_adapter/kitchen_sink1.rb +48 -0
- data/examples/coolio_adapter/queue_bind.rb +32 -0
- data/examples/coolio_adapter/queue_purge.rb +32 -0
- data/examples/coolio_adapter/queue_unbind.rb +37 -0
- data/examples/eventmachine_adapter/authentication/plain_password_with_custom_role_credentials.rb +36 -0
- data/examples/eventmachine_adapter/authentication/plain_password_with_default_role_credentials.rb +27 -0
- data/examples/eventmachine_adapter/authentication/plain_password_with_incorrect_credentials.rb +18 -0
- data/examples/eventmachine_adapter/basic_cancel.rb +49 -0
- data/examples/eventmachine_adapter/basic_consume.rb +51 -0
- data/examples/eventmachine_adapter/basic_consume_with_acknowledgements.rb +45 -0
- data/examples/eventmachine_adapter/basic_consume_with_rejections.rb +45 -0
- data/examples/eventmachine_adapter/basic_get.rb +57 -0
- data/examples/eventmachine_adapter/basic_get_with_empty_queue.rb +53 -0
- data/examples/eventmachine_adapter/basic_publish.rb +38 -0
- data/examples/eventmachine_adapter/basic_qos.rb +29 -0
- data/examples/eventmachine_adapter/basic_recover.rb +29 -0
- data/examples/eventmachine_adapter/basic_return.rb +34 -0
- data/examples/eventmachine_adapter/channel_close.rb +24 -0
- data/examples/eventmachine_adapter/channel_flow.rb +36 -0
- data/examples/eventmachine_adapter/channel_level_exception_handling.rb +44 -0
- data/examples/eventmachine_adapter/example_helper.rb +39 -0
- data/examples/eventmachine_adapter/exchange_declare.rb +54 -0
- data/examples/eventmachine_adapter/extensions/rabbitmq/handling_confirm_select_ok.rb +31 -0
- data/examples/eventmachine_adapter/extensions/rabbitmq/publisher_confirmations_with_transient_messages.rb +56 -0
- data/examples/eventmachine_adapter/extensions/rabbitmq/publisher_confirmations_with_unroutable_message.rb +46 -0
- data/examples/eventmachine_adapter/kitchen_sink1.rb +50 -0
- data/examples/eventmachine_adapter/queue_bind.rb +32 -0
- data/examples/eventmachine_adapter/queue_declare.rb +34 -0
- data/examples/eventmachine_adapter/queue_purge.rb +32 -0
- data/examples/eventmachine_adapter/queue_unbind.rb +37 -0
- data/examples/eventmachine_adapter/tx_commit.rb +29 -0
- data/examples/eventmachine_adapter/tx_rollback.rb +29 -0
- data/examples/eventmachine_adapter/tx_select.rb +27 -0
- data/examples/socket_adapter/basics.rb +19 -0
- data/examples/socket_adapter/connection.rb +53 -0
- data/examples/socket_adapter/multiple_connections.rb +17 -0
- data/irb.rb +66 -0
- data/lib/amq/client.rb +15 -0
- data/lib/amq/client/adapter.rb +356 -0
- data/lib/amq/client/adapters/coolio.rb +221 -0
- data/lib/amq/client/adapters/event_machine.rb +228 -0
- data/lib/amq/client/adapters/socket.rb +89 -0
- data/lib/amq/client/channel.rb +338 -0
- data/lib/amq/client/connection.rb +246 -0
- data/lib/amq/client/entity.rb +117 -0
- data/lib/amq/client/exceptions.rb +86 -0
- data/lib/amq/client/exchange.rb +163 -0
- data/lib/amq/client/extensions/rabbitmq.rb +5 -0
- data/lib/amq/client/extensions/rabbitmq/basic.rb +36 -0
- data/lib/amq/client/extensions/rabbitmq/confirm.rb +254 -0
- data/lib/amq/client/framing/io/frame.rb +32 -0
- data/lib/amq/client/framing/string/frame.rb +62 -0
- data/lib/amq/client/logging.rb +56 -0
- data/lib/amq/client/mixins/anonymous_entity.rb +21 -0
- data/lib/amq/client/mixins/status.rb +62 -0
- data/lib/amq/client/protocol/get_response.rb +55 -0
- data/lib/amq/client/queue.rb +450 -0
- data/lib/amq/client/settings.rb +83 -0
- data/lib/amq/client/version.rb +5 -0
- data/spec/benchmarks/adapters.rb +77 -0
- data/spec/client/framing/io_frame_spec.rb +57 -0
- data/spec/client/framing/string_frame_spec.rb +57 -0
- data/spec/client/protocol/get_response_spec.rb +79 -0
- data/spec/integration/coolio/basic_ack_spec.rb +41 -0
- data/spec/integration/coolio/basic_get_spec.rb +73 -0
- data/spec/integration/coolio/basic_return_spec.rb +33 -0
- data/spec/integration/coolio/channel_close_spec.rb +26 -0
- data/spec/integration/coolio/channel_flow_spec.rb +46 -0
- data/spec/integration/coolio/spec_helper.rb +31 -0
- data/spec/integration/coolio/tx_commit_spec.rb +40 -0
- data/spec/integration/coolio/tx_rollback_spec.rb +44 -0
- data/spec/integration/eventmachine/basic_ack_spec.rb +40 -0
- data/spec/integration/eventmachine/basic_get_spec.rb +73 -0
- data/spec/integration/eventmachine/basic_return_spec.rb +35 -0
- data/spec/integration/eventmachine/channel_close_spec.rb +26 -0
- data/spec/integration/eventmachine/channel_flow_spec.rb +32 -0
- data/spec/integration/eventmachine/spec_helper.rb +22 -0
- data/spec/integration/eventmachine/tx_commit_spec.rb +47 -0
- data/spec/integration/eventmachine/tx_rollback_spec.rb +35 -0
- data/spec/regression/bad_frame_slicing_in_adapters_spec.rb +59 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/unit/client/adapter_spec.rb +49 -0
- data/spec/unit/client/entity_spec.rb +49 -0
- data/spec/unit/client/logging_spec.rb +60 -0
- data/spec/unit/client/mixins/status_spec.rb +72 -0
- data/spec/unit/client/settings_spec.rb +27 -0
- data/spec/unit/client_spec.rb +11 -0
- data/tasks.rb +11 -0
- metadata +202 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
# http://coolio.github.com
|
|
4
|
+
|
|
5
|
+
require "cool.io"
|
|
6
|
+
require "amq/client"
|
|
7
|
+
|
|
8
|
+
require "amq/client/framing/string/frame"
|
|
9
|
+
|
|
10
|
+
module AMQ
|
|
11
|
+
module Client
|
|
12
|
+
class Coolio
|
|
13
|
+
class Socket < ::Coolio::TCPSocket
|
|
14
|
+
attr_accessor :adapter
|
|
15
|
+
|
|
16
|
+
def self.connect(adapter, host, port)
|
|
17
|
+
socket = super(host, port)
|
|
18
|
+
socket.adapter = adapter
|
|
19
|
+
socket
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def on_connect
|
|
23
|
+
#puts "On connect"
|
|
24
|
+
adapter.on_socket_connect
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def on_read(data)
|
|
28
|
+
# puts "Received data"
|
|
29
|
+
# puts_data(data)
|
|
30
|
+
adapter.on_read(data)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# This handler should never trigger in normal circumstances
|
|
34
|
+
def on_close
|
|
35
|
+
adapter.on_socket_disconnect
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def send_raw(data)
|
|
39
|
+
# puts "Sending data"
|
|
40
|
+
# puts_data(data)
|
|
41
|
+
write(data)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
protected
|
|
45
|
+
def puts_data(data)
|
|
46
|
+
puts " As string: #{data.inspect}"
|
|
47
|
+
puts " As byte array: #{data.bytes.to_a.inspect}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Behaviors
|
|
52
|
+
include AMQ::Client::Adapter
|
|
53
|
+
|
|
54
|
+
self.sync = false
|
|
55
|
+
|
|
56
|
+
# API
|
|
57
|
+
attr_accessor :socket
|
|
58
|
+
attr_accessor :callbacks
|
|
59
|
+
attr_accessor :connections
|
|
60
|
+
|
|
61
|
+
class << self
|
|
62
|
+
def connect(settings, &block)
|
|
63
|
+
settings = self.settings.merge(settings)
|
|
64
|
+
host, port = settings[:host], settings[:port]
|
|
65
|
+
instance = new
|
|
66
|
+
socket = Socket.connect(instance, settings[:host], settings[:port])
|
|
67
|
+
socket.attach Cool.io::Loop.default
|
|
68
|
+
instance.socket = socket
|
|
69
|
+
instance.on_connection(&block)
|
|
70
|
+
instance
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def initialize
|
|
75
|
+
# Be careful with default values for #ruby hashes: h = Hash.new(Array.new); h[:key] ||= 1
|
|
76
|
+
# won't assign anything to :key. MK.
|
|
77
|
+
@callbacks = {}
|
|
78
|
+
@connections = []
|
|
79
|
+
super
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# sets a callback for connection
|
|
83
|
+
def on_connection(&block)
|
|
84
|
+
define_callback :connect, &block
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# sets a callback for disconnection
|
|
88
|
+
def on_disconnection(&block)
|
|
89
|
+
define_callback :disconnect, &block
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def on_open(&block)
|
|
93
|
+
define_callback :open, &block
|
|
94
|
+
end # on_open(&block)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# called by AMQ::Client::Connection after we receive connection.open-ok.
|
|
98
|
+
def connection_successful
|
|
99
|
+
exec_callback_yielding_self(:connect)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# called by AMQ::Client::Connection after we receive connection.close-ok.
|
|
103
|
+
def disconnection_successful
|
|
104
|
+
exec_callback_yielding_self(:disconnect)
|
|
105
|
+
close_connection
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def open_successful
|
|
109
|
+
@authenticating = false
|
|
110
|
+
exec_callback_yielding_self(:open)
|
|
111
|
+
end # open_successful
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# triggered when socket is connected but before handshake is done
|
|
115
|
+
def on_socket_connect
|
|
116
|
+
post_init
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# triggered after socket is closed
|
|
120
|
+
def on_socket_disconnect
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def send_raw(data)
|
|
124
|
+
socket.send_raw data
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# The story about the buffering is kinda similar to EventMachine,
|
|
128
|
+
# you keep receiving more than one frame in a single packet.
|
|
129
|
+
def on_read(chunk)
|
|
130
|
+
@chunk_buffer << chunk
|
|
131
|
+
while frame = get_next_frame
|
|
132
|
+
receive_frame(AMQ::Client::Framing::String::Frame.decode(frame))
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def close_connection
|
|
137
|
+
@socket.close
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
#
|
|
143
|
+
# Callbacks
|
|
144
|
+
#
|
|
145
|
+
|
|
146
|
+
def redefine_callback(event, callable = nil, &block)
|
|
147
|
+
f = (callable || block)
|
|
148
|
+
# yes, re-assign!
|
|
149
|
+
@callbacks[event] = [f]
|
|
150
|
+
|
|
151
|
+
self
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def define_callback(event, callable = nil, &block)
|
|
155
|
+
f = (callable || block)
|
|
156
|
+
@callbacks[event] ||= []
|
|
157
|
+
|
|
158
|
+
@callbacks[event] << f if f
|
|
159
|
+
|
|
160
|
+
self
|
|
161
|
+
end # define_callback(event, &block)
|
|
162
|
+
alias append_callback define_callback
|
|
163
|
+
|
|
164
|
+
def prepend_callback(event, &block)
|
|
165
|
+
@callbacks[event] ||= []
|
|
166
|
+
@callbacks[event].unshift(block)
|
|
167
|
+
|
|
168
|
+
self
|
|
169
|
+
end # prepend_callback(event, &block)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def exec_callback(name, *args, &block)
|
|
173
|
+
callbacks = Array(self.callbacks[name])
|
|
174
|
+
callbacks.map { |c| c.call(*args, &block) } if callbacks.any?
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def exec_callback_once(name, *args, &block)
|
|
178
|
+
callbacks = Array(self.callbacks.delete(name))
|
|
179
|
+
callbacks.map { |c| c.call(*args, &block) } if callbacks.any?
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def exec_callback_yielding_self(name, *args, &block)
|
|
183
|
+
callbacks = Array(self.callbacks[name])
|
|
184
|
+
callbacks.map { |c| c.call(self, *args, &block) } if callbacks.any?
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def exec_callback_once_yielding_self(name, *args, &block)
|
|
188
|
+
callbacks = Array(self.callbacks.delete(name))
|
|
189
|
+
callbacks.map { |c| c.call(self, *args, &block) } if callbacks.any?
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
protected
|
|
195
|
+
|
|
196
|
+
def post_init
|
|
197
|
+
reset
|
|
198
|
+
handshake
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def reset
|
|
202
|
+
@chunk_buffer = ""
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def get_next_frame
|
|
206
|
+
return nil unless @chunk_buffer.size > 7 # otherwise, cannot read the length
|
|
207
|
+
# octet + short
|
|
208
|
+
offset = 1 + 2
|
|
209
|
+
# length
|
|
210
|
+
payload_length = @chunk_buffer[offset, 4].unpack('N')[0]
|
|
211
|
+
# 4 bytes for long payload length, 1 byte final octet
|
|
212
|
+
frame_length = offset + 4 + payload_length + 1
|
|
213
|
+
if frame_length <= @chunk_buffer.size
|
|
214
|
+
@chunk_buffer.slice!(0, frame_length)
|
|
215
|
+
else
|
|
216
|
+
nil
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require "amq/client"
|
|
4
|
+
require "amq/client/channel"
|
|
5
|
+
require "amq/client/exchange"
|
|
6
|
+
require "amq/client/framing/string/frame"
|
|
7
|
+
|
|
8
|
+
require "eventmachine"
|
|
9
|
+
|
|
10
|
+
module AMQ
|
|
11
|
+
module Client
|
|
12
|
+
class EventMachineClient < EM::Connection
|
|
13
|
+
|
|
14
|
+
class Deferrable
|
|
15
|
+
include EventMachine::Deferrable
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
#
|
|
19
|
+
# Behaviors
|
|
20
|
+
#
|
|
21
|
+
|
|
22
|
+
include AMQ::Client::Adapter
|
|
23
|
+
|
|
24
|
+
self.sync = false
|
|
25
|
+
|
|
26
|
+
register_entity :channel, AMQ::Client::Channel
|
|
27
|
+
register_entity :exchange, AMQ::Client::Exchange
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# API
|
|
31
|
+
#
|
|
32
|
+
|
|
33
|
+
def self.connect(settings = nil, &block)
|
|
34
|
+
settings = AMQ::Client::Settings.configure(settings)
|
|
35
|
+
instance = EM.connect(settings[:host], settings[:port], self, settings)
|
|
36
|
+
|
|
37
|
+
unless block.nil?
|
|
38
|
+
# delay calling block we were given till after we receive
|
|
39
|
+
# connection.open-ok. Connection will notify us when
|
|
40
|
+
# that happens.
|
|
41
|
+
instance.on_connection do
|
|
42
|
+
block.call(instance)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
instance
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
attr_reader :connections
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def initialize(*args)
|
|
54
|
+
super(*args)
|
|
55
|
+
|
|
56
|
+
# EventMachine::Connection's and Adapter's constructors arity
|
|
57
|
+
# make it easier to use *args. MK.
|
|
58
|
+
@settings = args.first
|
|
59
|
+
@connections = Array.new
|
|
60
|
+
@on_possible_authentication_failure = @settings[:on_possible_authentication_failure]
|
|
61
|
+
|
|
62
|
+
@chunk_buffer = ""
|
|
63
|
+
@connection_deferrable = Deferrable.new
|
|
64
|
+
@disconnection_deferrable = Deferrable.new
|
|
65
|
+
|
|
66
|
+
@authenticating = false
|
|
67
|
+
|
|
68
|
+
# succeeds when connection is open, that is, vhost is selected
|
|
69
|
+
# and client is given green light to proceed.
|
|
70
|
+
@connection_opened_deferrable = Deferrable.new
|
|
71
|
+
|
|
72
|
+
@tcp_connection_established = false
|
|
73
|
+
|
|
74
|
+
if self.heartbeat_interval > 0
|
|
75
|
+
@last_server_heartbeat = Time.now
|
|
76
|
+
EM.add_periodic_timer(self.heartbeat_interval, &method(:send_heartbeat))
|
|
77
|
+
end
|
|
78
|
+
end # initialize(*args)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def establish_connection(settings)
|
|
82
|
+
# an intentional no-op
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
alias send_raw send_data
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def authenticating?
|
|
89
|
+
@authenticating
|
|
90
|
+
end # authenticating?
|
|
91
|
+
|
|
92
|
+
def tcp_connection_established?
|
|
93
|
+
@tcp_connection_established
|
|
94
|
+
end # tcp_connection_established?
|
|
95
|
+
|
|
96
|
+
#
|
|
97
|
+
# Implementation
|
|
98
|
+
#
|
|
99
|
+
|
|
100
|
+
def post_init
|
|
101
|
+
reset
|
|
102
|
+
|
|
103
|
+
@tcp_connection_established = true
|
|
104
|
+
|
|
105
|
+
self.handshake
|
|
106
|
+
rescue Exception => error
|
|
107
|
+
raise error
|
|
108
|
+
end # post_init
|
|
109
|
+
|
|
110
|
+
#
|
|
111
|
+
# EventMachine receives data in chunks, sometimes those chunks are smaller
|
|
112
|
+
# than the size of AMQP frame. That's why you need to add some kind of buffer.
|
|
113
|
+
#
|
|
114
|
+
def receive_data(chunk)
|
|
115
|
+
@chunk_buffer << chunk
|
|
116
|
+
while frame = get_next_frame
|
|
117
|
+
self.receive_frame(AMQ::Client::Framing::String::Frame.decode(frame))
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def unbind
|
|
122
|
+
closing!
|
|
123
|
+
|
|
124
|
+
@tcp_connection_established = false
|
|
125
|
+
|
|
126
|
+
@connections.each { |c| c.on_connection_interruption }
|
|
127
|
+
@disconnection_deferrable.succeed
|
|
128
|
+
|
|
129
|
+
closed!
|
|
130
|
+
|
|
131
|
+
# since AMQP spec dictates that authentication failure is a protocol exception
|
|
132
|
+
# and protocol exceptions result in connection closure, check whether we are
|
|
133
|
+
# in the authentication stage. If so, it is likely to signal an authentication
|
|
134
|
+
# issue. Java client behaves the same way. MK.
|
|
135
|
+
if authenticating?
|
|
136
|
+
if sync?
|
|
137
|
+
raise PossibleAuthenticationFailureError.new(@settings)
|
|
138
|
+
else
|
|
139
|
+
@on_possible_authentication_failure.call(@settings) if @on_possible_authentication_failure
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end # unbind
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def on_connection(&block)
|
|
147
|
+
@connection_deferrable.callback(&block)
|
|
148
|
+
end # on_connection(&block)
|
|
149
|
+
|
|
150
|
+
# called by AMQ::Client::Connection after we receive connection.open-ok.
|
|
151
|
+
def connection_successful
|
|
152
|
+
@connection_deferrable.succeed
|
|
153
|
+
end # connection_successful
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def on_open(&block)
|
|
157
|
+
@connection_opened_deferrable.callback(&block)
|
|
158
|
+
end # on_open(&block)
|
|
159
|
+
|
|
160
|
+
def open_successful
|
|
161
|
+
@authenticating = false
|
|
162
|
+
@connection_opened_deferrable.succeed
|
|
163
|
+
|
|
164
|
+
opened!
|
|
165
|
+
end # open_successful
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def on_disconnection(&block)
|
|
169
|
+
@disconnection_deferrable.callback(&block)
|
|
170
|
+
end # on_disconnection(&block)
|
|
171
|
+
|
|
172
|
+
# called by AMQ::Client::Connection after we receive connection.close-ok.
|
|
173
|
+
def disconnection_successful
|
|
174
|
+
@disconnection_deferrable.succeed
|
|
175
|
+
|
|
176
|
+
self.close_connection
|
|
177
|
+
closed!
|
|
178
|
+
end # disconnection_successful
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def on_possible_authentication_failure(&block)
|
|
183
|
+
@on_possible_authentication_failure = block
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
protected
|
|
188
|
+
|
|
189
|
+
def handshake(mechanism = "PLAIN", response = nil, locale = "en_GB")
|
|
190
|
+
username = @settings[:user] || @settings[:username]
|
|
191
|
+
password = @settings[:pass] || @settings[:password]
|
|
192
|
+
|
|
193
|
+
self.logger.info "[authentication] Credentials are #{username}/#{'*' * password.bytesize}"
|
|
194
|
+
|
|
195
|
+
self.connection = AMQ::Client::Connection.new(self, mechanism, self.encode_credentials(username, password), locale)
|
|
196
|
+
|
|
197
|
+
@authenticating = true
|
|
198
|
+
self.send_preamble
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def reset
|
|
202
|
+
@size = 0
|
|
203
|
+
@payload = ""
|
|
204
|
+
@frames = Array.new
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# @see http://tools.ietf.org/rfc/rfc2595.txt RFC 2595
|
|
208
|
+
def encode_credentials(username, password)
|
|
209
|
+
"\0#{username}\0#{password}"
|
|
210
|
+
end # encode_credentials(username, password)
|
|
211
|
+
|
|
212
|
+
def get_next_frame
|
|
213
|
+
return unless @chunk_buffer.size > 7 # otherwise, cannot read the length
|
|
214
|
+
# octet + short
|
|
215
|
+
offset = 3 # 1 + 2
|
|
216
|
+
# length
|
|
217
|
+
payload_length = @chunk_buffer[offset, 4].unpack('N')[0]
|
|
218
|
+
# 5: 4 bytes for long payload length, 1 byte final octet
|
|
219
|
+
frame_length = offset + 5 + payload_length
|
|
220
|
+
if frame_length <= @chunk_buffer.size
|
|
221
|
+
@chunk_buffer.slice!(0, frame_length)
|
|
222
|
+
else
|
|
223
|
+
nil
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end # EventMachineClient
|
|
227
|
+
end # Client
|
|
228
|
+
end # AMQ
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require "socket"
|
|
4
|
+
require "amq/client"
|
|
5
|
+
require "amq/client/channel"
|
|
6
|
+
require "amq/client/exchange"
|
|
7
|
+
require "amq/client/queue"
|
|
8
|
+
require "amq/client/framing/io/frame"
|
|
9
|
+
|
|
10
|
+
module AMQ
|
|
11
|
+
module Client
|
|
12
|
+
class SocketClient
|
|
13
|
+
|
|
14
|
+
#
|
|
15
|
+
# Behaviors
|
|
16
|
+
#
|
|
17
|
+
|
|
18
|
+
include AMQ::Client::Adapter
|
|
19
|
+
|
|
20
|
+
self.sync = true
|
|
21
|
+
|
|
22
|
+
register_entity :channel, AMQ::Client::Channel
|
|
23
|
+
register_entity :exchange, AMQ::Client::Exchange
|
|
24
|
+
register_entity :queue, AMQ::Client::Queue
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
#
|
|
28
|
+
# API
|
|
29
|
+
#
|
|
30
|
+
|
|
31
|
+
def establish_connection(settings)
|
|
32
|
+
# NOTE: this doesn't work with "localhost", I don't know why:
|
|
33
|
+
settings[:host] = "127.0.0.1" if settings[:host] == "localhost"
|
|
34
|
+
@socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
|
35
|
+
sockaddr = Socket.pack_sockaddr_in(settings[:port], settings[:host])
|
|
36
|
+
|
|
37
|
+
@socket.connect(sockaddr)
|
|
38
|
+
rescue Errno::ECONNREFUSED => exception
|
|
39
|
+
message = "Don't forget to start an AMQP broker first!\nThe original message: #{exception.message}"
|
|
40
|
+
raise exception.class.new(message)
|
|
41
|
+
rescue Exception => exception
|
|
42
|
+
self.disconnect if self.connected?
|
|
43
|
+
raise exception
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def connection
|
|
47
|
+
@socket
|
|
48
|
+
end # connection
|
|
49
|
+
|
|
50
|
+
def connected?
|
|
51
|
+
@socket && !@socket.closed?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def close_connection
|
|
55
|
+
@socket.close
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def send_raw(data)
|
|
59
|
+
@socket.write(data)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def receive
|
|
63
|
+
frame = AMQ::Client::Framing::IO::Frame.decode(@socket)
|
|
64
|
+
self.receive_frame(frame)
|
|
65
|
+
frame
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def receive_async
|
|
69
|
+
# NOTE: this might work with Socket#eof? as well, it can be better ...
|
|
70
|
+
# self.receive unless @socket.eof?
|
|
71
|
+
|
|
72
|
+
@sockets ||= [@socket] # It'll be always only one socket, but we don't want to create many arrays, mind the GC!
|
|
73
|
+
array = IO.select(@sockets, nil, nil, nil)
|
|
74
|
+
array[0].each do |socket|
|
|
75
|
+
res = self.receive
|
|
76
|
+
end
|
|
77
|
+
res
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def read_until_receives(klass)
|
|
81
|
+
if self.sync?
|
|
82
|
+
until (frame = self.receive) && frame.is_a?(Protocol::MethodFrame) && frame.method_class == klass
|
|
83
|
+
sleep 0.1
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|