amq-client 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|