qpid_proton 0.21.0 → 0.22.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.
- checksums.yaml +4 -4
- data/examples/README.md +26 -10
- data/examples/broker.rb +99 -91
- data/examples/example_test.rb +11 -11
- data/ext/cproton/cproton.c +3 -3
- data/lib/core/connection.rb +18 -12
- data/lib/core/connection_driver.rb +13 -3
- data/lib/core/container.rb +276 -120
- data/lib/core/disposition.rb +1 -1
- data/lib/core/endpoint.rb +3 -0
- data/lib/core/listener.rb +6 -0
- data/lib/core/message.rb +5 -9
- data/lib/core/messaging_handler.rb +5 -1
- data/lib/core/receiver.rb +1 -2
- data/lib/core/sasl.rb +1 -1
- data/lib/core/session.rb +6 -4
- data/lib/core/terminus.rb +10 -4
- data/lib/core/transfer.rb +3 -0
- data/lib/core/transport.rb +1 -0
- data/lib/core/uri.rb +9 -10
- data/lib/core/work_queue.rb +76 -0
- data/lib/qpid_proton.rb +2 -8
- data/lib/types/array.rb +1 -0
- data/lib/types/hash.rb +0 -11
- data/lib/util/schedule.rb +79 -0
- data/lib/util/wrapper.rb +4 -0
- data/tests/old_examples/old_example_test.rb +0 -8
- data/tests/test_container.rb +203 -126
- data/tests/test_container_sasl.rb +141 -0
- data/tests/test_interop.rb +1 -1
- data/tests/test_messaging_adapter.rb +1 -1
- data/tests/test_tools.rb +30 -5
- data/tests/test_uri.rb +2 -0
- metadata +5 -9
- data/lib/messenger/messenger.rb +0 -703
- data/lib/messenger/subscription.rb +0 -36
- data/lib/messenger/tracker.rb +0 -37
- data/lib/messenger/tracker_status.rb +0 -68
- data/lib/util/timeout.rb +0 -49
- data/tests/old_examples/recv.rb +0 -23
- data/tests/old_examples/send.rb +0 -21
data/tests/test_container.rb
CHANGED
@@ -20,26 +20,26 @@ require 'test_tools'
|
|
20
20
|
require 'minitest/unit'
|
21
21
|
require 'socket'
|
22
22
|
|
23
|
-
#
|
24
|
-
class
|
25
|
-
|
26
|
-
def initialize(handler, lopts=nil, id=nil)
|
27
|
-
super handler, id
|
28
|
-
@listener = listen_io(TCPServer.open(0), ListenOnceHandler.new(lopts))
|
29
|
-
end
|
30
|
-
|
31
|
-
def port() @listener.to_io.addr[1]; end
|
32
|
-
def url() "amqp://:#{port}"; end#
|
23
|
+
# MessagingHandler that raises in on_error to catch unexpected errors
|
24
|
+
class ExceptionMessagingHandler
|
25
|
+
def on_error(e) raise e; end
|
33
26
|
end
|
34
27
|
|
35
28
|
class ContainerTest < MiniTest::Test
|
36
29
|
include Qpid::Proton
|
37
30
|
|
38
31
|
def test_simple()
|
39
|
-
send_handler = Class.new(
|
32
|
+
send_handler = Class.new(ExceptionMessagingHandler) do
|
40
33
|
attr_reader :accepted, :sent
|
34
|
+
|
35
|
+
def initialize() @sent, @accepted = nil; end
|
36
|
+
|
41
37
|
def on_sendable(sender)
|
42
|
-
|
38
|
+
unless @sent
|
39
|
+
m = Message.new("hello")
|
40
|
+
m[:foo] = :bar
|
41
|
+
sender.send m
|
42
|
+
end
|
43
43
|
@sent = true
|
44
44
|
end
|
45
45
|
|
@@ -49,7 +49,7 @@ class ContainerTest < MiniTest::Test
|
|
49
49
|
end
|
50
50
|
end.new
|
51
51
|
|
52
|
-
receive_handler = Class.new(
|
52
|
+
receive_handler = Class.new(ExceptionMessagingHandler) do
|
53
53
|
attr_reader :message, :link
|
54
54
|
def on_receiver_open(link)
|
55
55
|
@link = link
|
@@ -64,13 +64,14 @@ class ContainerTest < MiniTest::Test
|
|
64
64
|
end
|
65
65
|
end.new
|
66
66
|
|
67
|
-
c =
|
67
|
+
c = ServerContainer.new(__method__, {:handler => receive_handler})
|
68
68
|
c.connect(c.url, {:handler => send_handler}).open_sender({:name => "testlink"})
|
69
69
|
c.run
|
70
70
|
|
71
71
|
assert send_handler.accepted
|
72
72
|
assert_equal "testlink", receive_handler.link.name
|
73
|
-
assert_equal "
|
73
|
+
assert_equal "hello", receive_handler.message.body
|
74
|
+
assert_equal :bar, receive_handler.message[:foo]
|
74
75
|
assert_equal "test_simple", receive_handler.link.connection.container_id
|
75
76
|
end
|
76
77
|
|
@@ -153,48 +154,48 @@ class ContainerTest < MiniTest::Test
|
|
153
154
|
def test_bad_host
|
154
155
|
cont = Container.new(__method__)
|
155
156
|
assert_raises (SocketError) { cont.listen("badlisten.example.com:999") }
|
156
|
-
assert_raises (SocketError) {
|
157
|
+
assert_raises (SocketError) { cont.connect("badconnect.example.com:999") }
|
157
158
|
end
|
158
159
|
|
159
|
-
# Verify that connection options are sent to the peer
|
160
|
+
# Verify that connection options are sent to the peer
|
160
161
|
def test_connection_options
|
161
162
|
# Note: user, password and sasl_xxx options are tested by ContainerSASLTest below
|
162
|
-
server_handler = Class.new(
|
163
|
-
def on_error(e) raise e.inspect; end
|
163
|
+
server_handler = Class.new(ExceptionMessagingHandler) do
|
164
164
|
def on_connection_open(c)
|
165
165
|
@connection = c
|
166
166
|
c.open({
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
167
|
+
:virtual_host => "server.to.client",
|
168
|
+
:properties => { :server => :client },
|
169
|
+
:offered_capabilities => [ :s1 ],
|
170
|
+
:desired_capabilities => [ :s2 ],
|
171
|
+
:container_id => "box",
|
172
|
+
})
|
173
173
|
c.close
|
174
174
|
end
|
175
175
|
attr_reader :connection
|
176
176
|
end.new
|
177
|
-
# Transport options
|
178
|
-
cont =
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
177
|
+
# Transport options set by listener, by Connection#open it is too late
|
178
|
+
cont = ServerContainer.new(__method__, {
|
179
|
+
:handler => server_handler,
|
180
|
+
:idle_timeout => 88,
|
181
|
+
:max_sessions =>1000,
|
182
|
+
:max_frame_size => 8888,
|
183
|
+
})
|
184
184
|
client = cont.connect(cont.url,
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
185
|
+
{:virtual_host => "client.to.server",
|
186
|
+
:properties => { "foo" => :bar, "str" => "str" },
|
187
|
+
:offered_capabilities => [:c1 ],
|
188
|
+
:desired_capabilities => [:c2 ],
|
189
|
+
:idle_timeout => 42,
|
190
|
+
:max_sessions =>100,
|
191
|
+
:max_frame_size => 4096,
|
192
|
+
:container_id => "bowl"
|
193
|
+
})
|
194
194
|
cont.run
|
195
|
+
|
195
196
|
c = server_handler.connection
|
196
197
|
assert_equal "client.to.server", c.virtual_host
|
197
|
-
assert_equal({
|
198
|
+
assert_equal({ "foo" => :bar, "str" => "str" }, c.properties)
|
198
199
|
assert_equal([:c1], c.offered_capabilities)
|
199
200
|
assert_equal([:c2], c.desired_capabilities)
|
200
201
|
assert_equal 21, c.idle_timeout # Proton divides by 2
|
@@ -212,108 +213,184 @@ class ContainerTest < MiniTest::Test
|
|
212
213
|
assert_equal 44, c.idle_timeout # Proton divides by 2
|
213
214
|
assert_equal 100, c.max_sessions
|
214
215
|
end
|
215
|
-
end
|
216
|
-
|
217
216
|
|
218
|
-
|
219
|
-
|
217
|
+
def test_link_options
|
218
|
+
server_handler = Class.new(ExceptionMessagingHandler) do
|
219
|
+
def initialize() @links = []; end
|
220
|
+
attr_reader :links
|
221
|
+
def on_sender_open(l) @links << l; end
|
222
|
+
def on_receiver_open(l) @links << l; end
|
223
|
+
end.new
|
220
224
|
|
221
|
-
|
222
|
-
|
225
|
+
client_handler = Class.new(ExceptionMessagingHandler) do
|
226
|
+
def on_connection_open(c)
|
227
|
+
@links = [];
|
228
|
+
@links << c.open_sender("s1")
|
229
|
+
@links << c.open_sender({:name => "s2-n", :target => "s2-t", :source => "s2-s"})
|
230
|
+
@links << c.open_receiver("r1")
|
231
|
+
@links << c.open_receiver({:name => "r2-n", :target => "r2-t", :source => "r2-s"})
|
232
|
+
c.close
|
233
|
+
end
|
234
|
+
attr_reader :links
|
235
|
+
end.new
|
223
236
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
end
|
237
|
+
cont = ServerContainer.new(__method__, {:handler => server_handler }, 1)
|
238
|
+
cont.connect(cont.url, :handler => client_handler)
|
239
|
+
cont.run
|
228
240
|
|
229
|
-
|
230
|
-
|
231
|
-
|
241
|
+
expect = ["test_link_options/1", "s2-n", "test_link_options/2", "r2-n"]
|
242
|
+
assert_equal expect, server_handler.links.map(&:name)
|
243
|
+
assert_equal expect, client_handler.links.map(&:name)
|
232
244
|
|
233
|
-
|
245
|
+
expect = [[nil,"s1"], ["s2-s","s2-t"], ["r1",nil], ["r2-s","r2-t"]]
|
246
|
+
assert_equal expect, server_handler.links.map { |l| [l.remote_source.address, l.remote_target.address] }
|
247
|
+
assert_equal expect, client_handler.links.map { |l| [l.source.address, l.target.address] }
|
248
|
+
end
|
234
249
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
end
|
242
|
-
end
|
250
|
+
def extract_terminus_options(t)
|
251
|
+
opts = Hash[[:address, :distribution_mode, :durability_mode, :timeout, :expiry_policy].map { |m| [m, t.send(m)] }]
|
252
|
+
opts[:filter] = t.filter.map
|
253
|
+
opts[:capabilities] = t.capabilities.map
|
254
|
+
opts[:dynamic] = t.dynamic?
|
255
|
+
opts
|
243
256
|
end
|
244
257
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
end
|
266
|
-
# Tell proton library to use the new configuration
|
267
|
-
SASL.config_path = conf_dir
|
268
|
-
SASL.config_name = conf_name
|
269
|
-
end
|
270
|
-
end
|
258
|
+
def test_terminus_options
|
259
|
+
opts = {
|
260
|
+
:distribution_mode => Terminus::DIST_MODE_COPY,
|
261
|
+
:durability_mode => Terminus::DELIVERIES,
|
262
|
+
:timeout => 5,
|
263
|
+
:expiry_policy => Terminus::EXPIRE_WITH_LINK,
|
264
|
+
:filter => { :try => 'me' },
|
265
|
+
:capabilities => { :cap => 'len' },
|
266
|
+
}
|
267
|
+
src_opts = { :address => "src", :dynamic => true }.update(opts)
|
268
|
+
tgt_opts = { :address => "tgt", :dynamic => false }.update(opts)
|
269
|
+
|
270
|
+
cont = ServerContainer.new(__method__, {}, 1)
|
271
|
+
c = cont.connect(cont.url)
|
272
|
+
s = c.open_sender({:target => tgt_opts, :source => src_opts })
|
273
|
+
assert_equal src_opts, extract_terminus_options(s.source)
|
274
|
+
assert_equal tgt_opts, extract_terminus_options(s.target)
|
275
|
+
assert s.source.dynamic?
|
276
|
+
assert !s.target.dynamic?
|
277
|
+
end
|
271
278
|
|
272
|
-
|
279
|
+
# Test for time out on connecting to an unresponsive server
|
280
|
+
def test_idle_timeout_server_no_open
|
281
|
+
s = TCPServer.new(0)
|
282
|
+
cont = Container.new(__method__)
|
283
|
+
cont.connect(":#{s.addr[1]}", {:idle_timeout => 0.1, :handler => ExceptionMessagingHandler.new })
|
284
|
+
ex = assert_raises(Qpid::Proton::Condition) { cont.run }
|
285
|
+
assert_match(/resource-limit-exceeded/, ex.to_s)
|
286
|
+
ensure
|
287
|
+
s.close if s
|
288
|
+
end
|
273
289
|
|
274
|
-
|
290
|
+
# Test for time out on unresponsive client
|
291
|
+
def test_idle_timeout_client
|
292
|
+
server = ServerContainerThread.new("#{__method__}.server", {:idle_timeout => 0.1})
|
293
|
+
client_handler = Class.new(ExceptionMessagingHandler) do
|
294
|
+
def initialize() @ready, @block = Queue.new, Queue.new; end
|
295
|
+
attr_reader :ready, :block
|
296
|
+
def on_connection_open(c)
|
297
|
+
@ready.push nil # Tell the main thread we are now open
|
298
|
+
@block.pop # Block the client so the server will time it out
|
299
|
+
end
|
300
|
+
end.new
|
275
301
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
302
|
+
client = Container.new(nil, "#{__method__}.client")
|
303
|
+
client.connect(server.url, {:handler => client_handler})
|
304
|
+
client_thread = Thread.new { client.run }
|
305
|
+
client_handler.ready.pop # Wait till the client has connected
|
306
|
+
server.join # Exits when the connection closes from idle-timeout
|
307
|
+
client_handler.block.push nil # Unblock the client
|
308
|
+
ex = assert_raises(Qpid::Proton::Condition) { client_thread.join }
|
309
|
+
assert_match(/resource-limit-exceeded/, ex.to_s)
|
282
310
|
end
|
283
311
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
312
|
+
# Make sure we stop and clean up if an aborted connection causes a handler to raise.
|
313
|
+
# https://issues.apache.org/jira/browse/PROTON-1791
|
314
|
+
def test_handler_raise
|
315
|
+
cont = ServerContainer.new(__method__, {}, 0) # Don't auto-close the listener
|
316
|
+
client_handler = Class.new(MessagingHandler) do
|
317
|
+
# TestException is < Exception so not handled by default rescue clause
|
318
|
+
def on_connection_open(c) raise TestException.new("Bad Dog"); end
|
319
|
+
end.new
|
320
|
+
threads = 3.times.collect { Thread.new { cont.run } }
|
321
|
+
sleep 0.01 while cont.running < 3 # Wait for all threads to be running
|
322
|
+
sockets = 2.times.collect { TCPSocket.new("", cont.port) }
|
323
|
+
cont.connect_io(sockets[1]) # No exception
|
324
|
+
cont.connect_io(sockets[0], {:handler => client_handler}) # Should stop container
|
325
|
+
|
326
|
+
threads.each { |t| assert_equal("Bad Dog", assert_raises(TestException) {t.join}.message) }
|
327
|
+
sockets.each { |s| assert s.closed? }
|
328
|
+
assert cont.listener.to_io.closed?
|
329
|
+
assert_raises(Container::StoppedError) { cont.run }
|
330
|
+
assert_raises(Container::StoppedError) { cont.listen "" }
|
288
331
|
end
|
289
332
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
assert_equal
|
297
|
-
assert_equal
|
333
|
+
# Make sure Container::Scheduler puts tasks in proper order.
|
334
|
+
def test_scheduler
|
335
|
+
a = []
|
336
|
+
s = Schedule.new
|
337
|
+
|
338
|
+
assert_equal true, s.add(Time.at 3) { a << 3 }
|
339
|
+
assert_equal false, s.process(Time.at 2) # Should not run
|
340
|
+
assert_equal [], a
|
341
|
+
assert_equal true, s.process(Time.at 3) # Should run
|
342
|
+
assert_equal [3], a
|
343
|
+
|
344
|
+
a = []
|
345
|
+
assert_equal true, s.add(Time.at 3) { a << 3 }
|
346
|
+
assert_equal false, s.add(Time.at 5) { a << 5 }
|
347
|
+
assert_equal false, s.add(Time.at 1) { a << 1 }
|
348
|
+
assert_equal false, s.add(Time.at 4) { a << 4 }
|
349
|
+
assert_equal false, s.add(Time.at 4) { a << 4.1 }
|
350
|
+
assert_equal false, s.add(Time.at 4) { a << 4.2 }
|
351
|
+
assert_equal false, s.process(Time.at 4)
|
352
|
+
assert_equal [1, 3, 4, 4.1, 4.2], a
|
353
|
+
a = []
|
354
|
+
assert_equal true, s.process(Time.at 5)
|
355
|
+
assert_equal [5], a
|
298
356
|
end
|
299
357
|
|
300
|
-
def
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
358
|
+
def test_container_schedule
|
359
|
+
c = Container.new __method__
|
360
|
+
delays = [0.1, 0.03, 0.02, 0.04]
|
361
|
+
a = []
|
362
|
+
delays.each { |d| c.schedule(d) { a << [d, Time.now] } }
|
363
|
+
start = Time.now
|
364
|
+
c.run
|
365
|
+
delays.sort.each do |d|
|
366
|
+
x = a.shift
|
367
|
+
assert_equal d, x[0]
|
368
|
+
assert_in_delta start + d, x[1], 0.01
|
369
|
+
end
|
309
370
|
end
|
310
371
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
372
|
+
def test_work_queue
|
373
|
+
cont = ServerContainer.new(__method__, {}, 1)
|
374
|
+
c = cont.connect(cont.url)
|
375
|
+
t = Thread.new { cont.run }
|
376
|
+
q = Queue.new
|
377
|
+
|
378
|
+
start = Time.now
|
379
|
+
c.work_queue.schedule(0.02) { q << [3, Thread.current] }
|
380
|
+
c.work_queue.add { q << [1, Thread.current] }
|
381
|
+
c.work_queue.schedule(0.04) { q << [4, Thread.current] }
|
382
|
+
c.work_queue.add { q << [2, Thread.current] }
|
383
|
+
|
384
|
+
assert_equal [1, t], q.pop
|
385
|
+
assert_equal [2, t], q.pop
|
386
|
+
assert_in_delta 0.0, Time.now - start, 0.01
|
387
|
+
assert_equal [3, t], q.pop
|
388
|
+
assert_in_delta 0.02, Time.now - start, 0.01
|
389
|
+
assert_equal [4, t], q.pop
|
390
|
+
assert_in_delta 0.04, Time.now - start, 0.01
|
391
|
+
|
392
|
+
c.work_queue.add { c.close }
|
393
|
+
t.join
|
394
|
+
assert_raises(EOFError) { c.work_queue.add { } }
|
317
395
|
end
|
318
396
|
end
|
319
|
-
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
3
|
+
# distributed with this work for additional information
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
6
|
+
# "License"); you may not use this file except in compliance
|
7
|
+
# with the License. You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
|
18
|
+
|
19
|
+
require 'test_tools'
|
20
|
+
require 'minitest/unit'
|
21
|
+
require 'socket'
|
22
|
+
|
23
|
+
# Container that listens on a random port
|
24
|
+
class TestContainer < Qpid::Proton::Container
|
25
|
+
|
26
|
+
def initialize(handler, listener_opts, id)
|
27
|
+
super handler, id
|
28
|
+
@listener = listen_io(TCPServer.open(0), ListenOnceHandler.new(listener_opts))
|
29
|
+
end
|
30
|
+
attr_reader :listener
|
31
|
+
end
|
32
|
+
|
33
|
+
class ContainerSASLTest < MiniTest::Test
|
34
|
+
include Qpid::Proton
|
35
|
+
|
36
|
+
# Handler for test client/server that sets up server and client SASL options
|
37
|
+
class SASLHandler < TestHandler
|
38
|
+
|
39
|
+
def initialize(url="amqp://", opts=nil)
|
40
|
+
super()
|
41
|
+
@url, @opts = url, opts
|
42
|
+
end
|
43
|
+
|
44
|
+
def on_container_start(container)
|
45
|
+
@client = container.connect("#{@url}:#{container.listener.port}", @opts)
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :auth_user
|
49
|
+
|
50
|
+
def on_connection_open(connection)
|
51
|
+
super
|
52
|
+
if connection == @client
|
53
|
+
connection.close
|
54
|
+
else
|
55
|
+
@auth_user = connection.user
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Generate SASL server configuration files and database, initialize proton SASL
|
61
|
+
class SASLConfig
|
62
|
+
include Qpid::Proton
|
63
|
+
attr_reader :conf_dir, :conf_file, :conf_name, :database, :error
|
64
|
+
|
65
|
+
def initialize()
|
66
|
+
if SASL.extended? # Configure cyrus SASL
|
67
|
+
@conf_dir = File.expand_path('sasl_conf')
|
68
|
+
@conf_name = "proton-server"
|
69
|
+
@database = File.join(@conf_dir, "proton.sasldb")
|
70
|
+
@conf_file = File.join(conf_dir,"#{@conf_name}.conf")
|
71
|
+
Dir::mkdir(@conf_dir) unless File.directory?(@conf_dir)
|
72
|
+
# Same user name in different realms
|
73
|
+
make_user("user", "password", "proton") # proton realm
|
74
|
+
make_user("user", "default_password") # Default realm
|
75
|
+
File.open(@conf_file, 'w') do |f|
|
76
|
+
f.write("
|
77
|
+
sasldb_path: #{database}
|
78
|
+
mech_list: EXTERNAL DIGEST-MD5 SCRAM-SHA-1 CRAM-MD5 PLAIN ANONYMOUS
|
79
|
+
")
|
80
|
+
end
|
81
|
+
# Tell proton library to use the new configuration
|
82
|
+
SASL.config_path = conf_dir
|
83
|
+
SASL.config_name = conf_name
|
84
|
+
end
|
85
|
+
rescue => e
|
86
|
+
@error = e
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
SASLPASSWD = (ENV['SASLPASSWD'] or 'saslpasswd2')
|
92
|
+
|
93
|
+
def make_user(user, password, realm=nil)
|
94
|
+
realm_opt = (realm ? "-u #{realm}" : "")
|
95
|
+
cmd = "echo '#{password}' | #{SASLPASSWD} -c -p -f #{database} #{realm_opt} #{user}"
|
96
|
+
system(cmd) or raise RuntimeError.new("saslpasswd2 failed: #{cmd}")
|
97
|
+
end
|
98
|
+
|
99
|
+
INSTANCE = SASLConfig.new
|
100
|
+
end
|
101
|
+
|
102
|
+
def begin_extended_test
|
103
|
+
skip("Extended SASL not enabled") unless SASL.extended?
|
104
|
+
skip("Extended SASL setup error: #{SASLConfig::INSTANCE.error}") if SASLConfig::INSTANCE.error
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_sasl_anonymous()
|
108
|
+
s = SASLHandler.new("amqp://", {:sasl_allowed_mechs => "ANONYMOUS"})
|
109
|
+
TestContainer.new(s, {:sasl_allowed_mechs => "ANONYMOUS"}, __method__).run
|
110
|
+
assert_equal "anonymous", s.connections[0].user
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_sasl_plain_url()
|
114
|
+
begin_extended_test
|
115
|
+
# Use default realm with URL, should authenticate with "default_password"
|
116
|
+
opts = {:sasl_allowed_mechs => "PLAIN", :sasl_allow_insecure_mechs => true}
|
117
|
+
s = SASLHandler.new("amqp://user:default_password@", opts)
|
118
|
+
TestContainer.new(s, opts, __method__).run
|
119
|
+
assert_equal(2, s.connections.size)
|
120
|
+
assert_equal("user", s.auth_user)
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_sasl_plain_options()
|
124
|
+
begin_extended_test
|
125
|
+
# Use default realm with connection options, should authenticate with "default_password"
|
126
|
+
opts = {:sasl_allowed_mechs => "PLAIN",:sasl_allow_insecure_mechs => true,
|
127
|
+
:user => 'user', :password => 'default_password' }
|
128
|
+
s = SASLHandler.new("amqp://", opts)
|
129
|
+
TestContainer.new(s, {:sasl_allowed_mechs => "PLAIN",:sasl_allow_insecure_mechs => true}, __method__).run
|
130
|
+
assert_equal(2, s.connections.size)
|
131
|
+
assert_equal("user", s.auth_user)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Ensure we don't allow PLAIN if allow_insecure_mechs = true is not explicitly set
|
135
|
+
def test_disallow_insecure()
|
136
|
+
# Don't set allow_insecure_mechs, but try to use PLAIN
|
137
|
+
s = SASLHandler.new("amqp://user:password@", {:sasl_allowed_mechs => "PLAIN", :sasl_allow_insecure_mechs => true})
|
138
|
+
e = assert_raises(TestError) { TestContainer.new(s, {:sasl_allowed_mechs => "PLAIN"}, __method__).run }
|
139
|
+
assert_match(/amqp:unauthorized-access.*Authentication failed/, e.to_s)
|
140
|
+
end
|
141
|
+
end
|
data/tests/test_interop.rb
CHANGED
@@ -23,7 +23,7 @@ class InteropTest < MiniTest::Test
|
|
23
23
|
# Walk up the directory tree to find the tests directory.
|
24
24
|
def get_data(name)
|
25
25
|
path = File.join(File.dirname(__FILE__), "../../../../tests/interop/#{name}.amqp")
|
26
|
-
raise "Can't find test/interop directory from #{__FILE__}" unless File.
|
26
|
+
raise "Can't find test/interop directory from #{__FILE__}" unless File.exist?(path)
|
27
27
|
File.open(path, "rb") { |f| f.read }
|
28
28
|
end
|
29
29
|
|
@@ -112,7 +112,7 @@ class TestMessagingHandler < MiniTest::Test
|
|
112
112
|
def test_session_error
|
113
113
|
d = DriverPair.new(RecordingHandler.new, RecordingHandler.new)
|
114
114
|
d.client.connection.open
|
115
|
-
s = d.client.connection.
|
115
|
+
s = d.client.connection.default_session; s.open; d.run
|
116
116
|
assert_equal [:on_connection_open, :on_session_open], d.client.handler.names
|
117
117
|
assert_equal [:on_connection_open, :on_session_open], d.server.handler.names
|
118
118
|
d.clear
|
data/tests/test_tools.rb
CHANGED
@@ -28,7 +28,8 @@ rescue NameError # For older versions of MiniTest
|
|
28
28
|
MiniTest::Test = MiniTest::Unit::TestCase
|
29
29
|
end
|
30
30
|
|
31
|
-
class TestError <
|
31
|
+
class TestError < RuntimeError; end # Normal error
|
32
|
+
class TestException < Exception; end # Not caught by default rescue
|
32
33
|
|
33
34
|
def wait_port(port, timeout=5)
|
34
35
|
deadline = Time.now + timeout
|
@@ -95,15 +96,15 @@ class TestHandler < Qpid::Proton::MessagingHandler
|
|
95
96
|
end
|
96
97
|
end
|
97
98
|
|
98
|
-
# ListenHandler that closes the Listener after first
|
99
|
+
# ListenHandler that closes the Listener after first (or n) accepts
|
99
100
|
class ListenOnceHandler < Qpid::Proton::Listener::Handler
|
100
|
-
def
|
101
|
-
def
|
101
|
+
def initialize(opts, n=1) super(opts); @n=n; end
|
102
|
+
def on_error(l, e) raise e; end
|
103
|
+
def on_accept(l) l.close if (@n -= 1).zero?; super; end
|
102
104
|
end
|
103
105
|
|
104
106
|
# Add port/url to Listener, assuming a TCP socket
|
105
107
|
class Qpid::Proton::Listener
|
106
|
-
def port() to_io.addr[1]; end
|
107
108
|
def url() "amqp://:#{port}"; end
|
108
109
|
end
|
109
110
|
|
@@ -145,3 +146,27 @@ DriverPair = Struct.new(:client, :server) do
|
|
145
146
|
end
|
146
147
|
end
|
147
148
|
|
149
|
+
# Container that listens on a random port
|
150
|
+
class ServerContainer < Qpid::Proton::Container
|
151
|
+
include Qpid::Proton
|
152
|
+
|
153
|
+
def initialize(id=nil, listener_opts=nil, n=1)
|
154
|
+
super id
|
155
|
+
@listener = listen_io(TCPServer.open(0), ListenOnceHandler.new(listener_opts, n))
|
156
|
+
end
|
157
|
+
|
158
|
+
attr_reader :listener
|
159
|
+
|
160
|
+
def port() @listener.port; end
|
161
|
+
def url() "amqp://:#{port}"; end
|
162
|
+
end
|
163
|
+
|
164
|
+
class ServerContainerThread < ServerContainer
|
165
|
+
def initialize(id=nil, listener_opts=nil, n=1)
|
166
|
+
super
|
167
|
+
@thread = Thread.new { run }
|
168
|
+
end
|
169
|
+
|
170
|
+
attr_reader :thread
|
171
|
+
def join() @thread.join; end
|
172
|
+
end
|
data/tests/test_uri.rb
CHANGED
@@ -67,6 +67,8 @@ class TestURI < MiniTest::Test
|
|
67
67
|
assert_equal URI("amqp://h:1"), uri("h:1")
|
68
68
|
assert_equal URI("amqp://h"), uri("h")
|
69
69
|
assert_equal URI("amqp://h"), uri("h:")
|
70
|
+
assert_equal ["amqp", nil, "", 5672, ""], uri_parts("")
|
71
|
+
assert_equal ["amqp", nil, "", 5672, ""], uri_parts(":")
|
70
72
|
assert_equal ["amqp", nil, "", 1, ""], uri_parts(":1")
|
71
73
|
assert_equal ["amqp", nil, "", 1, ""], uri_parts("amqp://:1")
|
72
74
|
assert_equal URI("amqp://[::1:2]:1"), uri("[::1:2]:1")
|