qpid_proton 0.19.0 → 0.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/examples/README.md +76 -0
- data/examples/broker.rb +167 -0
- data/examples/client.rb +79 -0
- data/examples/direct_recv.rb +61 -0
- data/examples/direct_send.rb +67 -0
- data/examples/example_test.rb +109 -0
- data/examples/helloworld.rb +57 -0
- data/examples/server.rb +70 -0
- data/examples/simple_recv.rb +57 -0
- data/examples/simple_send.rb +63 -0
- data/examples/ssl_certs/README.txt +24 -0
- data/examples/ssl_certs/tclient-certificate.p12 +0 -0
- data/examples/ssl_certs/tclient-certificate.pem +19 -0
- data/examples/ssl_certs/tclient-full.p12 +0 -0
- data/examples/ssl_certs/tclient-private-key.pem +30 -0
- data/examples/ssl_certs/tserver-certificate.p12 +0 -0
- data/examples/ssl_certs/tserver-certificate.pem +19 -0
- data/examples/ssl_certs/tserver-full.p12 +0 -0
- data/examples/ssl_certs/tserver-private-key.pem +30 -0
- data/examples/ssl_send.rb +70 -0
- data/ext/cproton/cproton.c +105 -74
- data/lib/core/container.rb +2 -1
- data/lib/core/ssl_domain.rb +1 -1
- data/lib/core/uri.rb +15 -9
- data/lib/handler/messaging_adapter.rb +20 -5
- data/tests/old_examples/broker.rb +200 -0
- data/tests/old_examples/client.rb +81 -0
- data/tests/old_examples/direct_recv.rb +64 -0
- data/tests/old_examples/direct_send.rb +63 -0
- data/tests/old_examples/helloworld.rb +72 -0
- data/tests/old_examples/helloworld_direct.rb +73 -0
- data/tests/old_examples/lib/debugging.rb +25 -0
- data/tests/old_examples/lib/driver.rb +68 -0
- data/tests/old_examples/lib/qpid_examples.rb +26 -0
- data/tests/old_examples/lib/selectable.rb +119 -0
- data/tests/old_examples/lib/send_and_receive.rb +89 -0
- data/tests/old_examples/old_example_test.rb +107 -0
- data/tests/old_examples/recv.rb +23 -0
- data/tests/old_examples/send.rb +21 -0
- data/tests/old_examples/server.rb +75 -0
- data/tests/old_examples/simple_recv.rb +57 -0
- data/tests/old_examples/simple_send.rb +54 -0
- data/tests/test_connection_driver.rb +134 -0
- data/tests/test_container.rb +319 -0
- data/tests/test_data.rb +66 -0
- data/tests/test_delivery.rb +110 -0
- data/tests/test_interop.rb +131 -0
- data/tests/test_messaging_adapter.rb +223 -0
- data/tests/test_old_adapter.rb +228 -0
- data/tests/test_tools.rb +147 -0
- data/tests/test_uri.rb +83 -0
- metadata +49 -3
@@ -0,0 +1,134 @@
|
|
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
|
+
# distributed under the License is distributed on an
|
12
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
13
|
+
# KIND, either express or implied. See the License for the
|
14
|
+
# specific language governing permissions and limitations
|
15
|
+
# under the License.
|
16
|
+
|
17
|
+
|
18
|
+
require 'test_tools'
|
19
|
+
require 'minitest/unit'
|
20
|
+
|
21
|
+
include Qpid::Proton
|
22
|
+
|
23
|
+
# Test delivery of raw proton events
|
24
|
+
|
25
|
+
class RawDriverTest < MiniTest::Test
|
26
|
+
|
27
|
+
# Raw handler to record all on_xxx calls
|
28
|
+
class RecordingHandler
|
29
|
+
def initialize() @calls =[]; end
|
30
|
+
def proton_adapter_class() nil; end # Raw adapter
|
31
|
+
attr_reader :calls
|
32
|
+
|
33
|
+
def method_missing(name, *args) respond_to_missing?(name) ? @calls << name : super; end
|
34
|
+
def respond_to_missing?(name, private=false); (/^on_/ =~ name); end
|
35
|
+
def respond_to?(name, all=false) super || respond_to_missing?(name); end # For ruby < 1.9.2
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_send_recv
|
39
|
+
send_class = Class.new do
|
40
|
+
def proton_adapter_class() nil; end # Raw adapter
|
41
|
+
attr_reader :outcome
|
42
|
+
def on_link_flow(event) event.sender.send Message.new("foo"); end
|
43
|
+
def on_delivery(event)
|
44
|
+
@outcome = event.delivery.state
|
45
|
+
event.connection.close;
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
recv_class = Class.new do
|
50
|
+
def proton_adapter_class() nil; end # Raw adapter
|
51
|
+
attr_reader :message
|
52
|
+
def on_connection_remote_open(event) event.context.open; end
|
53
|
+
def on_session_remote_open(event) event.context.open; end
|
54
|
+
def on_link_remote_open(event) event.link.open; event.link.flow(1); end
|
55
|
+
def on_delivery(event) @message = event.message; event.delivery.accept; end
|
56
|
+
end
|
57
|
+
|
58
|
+
d = DriverPair.new(send_class.new, recv_class.new)
|
59
|
+
d.client.connection.open(:container_id => __method__);
|
60
|
+
d.client.connection.open_sender()
|
61
|
+
d.run
|
62
|
+
assert_equal(d.server.handler.message.body, "foo")
|
63
|
+
assert_equal Transfer::ACCEPTED, d.client.handler.outcome
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_idle
|
67
|
+
|
68
|
+
d = DriverPair.new(RecordingHandler.new, RecordingHandler.new)
|
69
|
+
ms = 444
|
70
|
+
secs = Rational(ms, 1000) # Use rationals to keep it accurate
|
71
|
+
opts = {:idle_timeout => secs}
|
72
|
+
d.client.transport.apply(opts)
|
73
|
+
assert_equal(ms, d.client.transport.idle_timeout) # Transport converts to ms
|
74
|
+
d.server.transport.set_server
|
75
|
+
d.client.connection.open(opts)
|
76
|
+
|
77
|
+
start = Time.at(1) # Dummy timeline
|
78
|
+
tick = d.run start # Process all IO events
|
79
|
+
assert_equal(secs/4, tick - start)
|
80
|
+
assert_equal [:on_connection_init, :on_connection_local_open, :on_connection_bound], d.client.handler.calls
|
81
|
+
assert_equal [:on_connection_init, :on_connection_bound, :on_connection_remote_open, :on_transport], d.server.handler.calls
|
82
|
+
assert_equal (ms), d.client.transport.idle_timeout
|
83
|
+
assert_equal (ms/2), d.server.transport.remote_idle_timeout # proton changes the value
|
84
|
+
assert_equal (secs/2), d.server.connection.idle_timeout
|
85
|
+
|
86
|
+
# Now update the time till we get connections closing
|
87
|
+
d.each { |x| x.handler.calls.clear }
|
88
|
+
d.run(start + secs - 0.001) # Should nothing, timeout not reached
|
89
|
+
assert_equal [[],[]], d.collect { |x| x.handler.calls }
|
90
|
+
d.run(start + secs*2) # After 2x timeout, connections should close
|
91
|
+
assert_equal [[:on_transport_error, :on_transport_tail_closed, :on_transport_head_closed, :on_transport_closed], [:on_connection_remote_close, :on_transport_tail_closed, :on_transport_head_closed, :on_transport_closed]], d.collect { |x| x.handler.calls }
|
92
|
+
end
|
93
|
+
|
94
|
+
# Test each_session/each_link methods both with a block and returning Enumerator
|
95
|
+
def test_enumerators
|
96
|
+
connection = Connection.new()
|
97
|
+
(3.times.collect { connection.open_session }).each { |s|
|
98
|
+
s.open_sender; s.open_receiver
|
99
|
+
}
|
100
|
+
|
101
|
+
assert_equal 3, connection.each_session.to_a.size
|
102
|
+
assert_equal 6, connection.each_link.to_a.size
|
103
|
+
|
104
|
+
# Build Session => Set<Links> map using connection link enumerator
|
105
|
+
map1 = {}
|
106
|
+
connection.each_link { |l| map1[l.session] ||= Set.new; map1[l.session] << l }
|
107
|
+
assert_equal 3, map1.size
|
108
|
+
map1.each do |session,links|
|
109
|
+
assert_equal 2, links.size
|
110
|
+
links.each { |l| assert_equal session, l.session }
|
111
|
+
end
|
112
|
+
|
113
|
+
# Build Session => Set<Links> map using connection and session blocks
|
114
|
+
map2 = {}
|
115
|
+
connection.each_session do |session|
|
116
|
+
map2[session] = Set.new
|
117
|
+
session.each_link { |l| map2[session] << l }
|
118
|
+
end
|
119
|
+
assert_equal map1, map2
|
120
|
+
|
121
|
+
# Build Session => Set<Links> map using connection session and session enumerators
|
122
|
+
map3 = Hash[connection.each_session.collect { |s| [s, Set.new(s.each_link)] }]
|
123
|
+
assert_equal map1, map3
|
124
|
+
|
125
|
+
assert_equal [true, true, true], connection.each_sender.collect { |l| l.is_a? Sender }
|
126
|
+
assert_equal [true, true, true], connection.each_receiver.collect { |l| l.is_a? Receiver }
|
127
|
+
connection.each_session { |session|
|
128
|
+
assert_equal [true], session.each_sender.collect { |l| l.is_a? Sender }
|
129
|
+
assert_equal [true], session.each_receiver.collect { |l| l.is_a? Receiver }
|
130
|
+
}
|
131
|
+
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,319 @@
|
|
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, 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#
|
33
|
+
end
|
34
|
+
|
35
|
+
class ContainerTest < MiniTest::Test
|
36
|
+
include Qpid::Proton
|
37
|
+
|
38
|
+
def test_simple()
|
39
|
+
send_handler = Class.new(MessagingHandler) do
|
40
|
+
attr_reader :accepted, :sent
|
41
|
+
def on_sendable(sender)
|
42
|
+
sender.send Message.new("foo") unless @sent
|
43
|
+
@sent = true
|
44
|
+
end
|
45
|
+
|
46
|
+
def on_tracker_accept(tracker)
|
47
|
+
@accepted = true
|
48
|
+
tracker.connection.close
|
49
|
+
end
|
50
|
+
end.new
|
51
|
+
|
52
|
+
receive_handler = Class.new(MessagingHandler) do
|
53
|
+
attr_reader :message, :link
|
54
|
+
def on_receiver_open(link)
|
55
|
+
@link = link
|
56
|
+
@link.open
|
57
|
+
@link.flow(1)
|
58
|
+
end
|
59
|
+
|
60
|
+
def on_message(delivery, message)
|
61
|
+
@message = message;
|
62
|
+
delivery.update Disposition::ACCEPTED
|
63
|
+
delivery.settle
|
64
|
+
end
|
65
|
+
end.new
|
66
|
+
|
67
|
+
c = TestContainer.new(receive_handler, {}, __method__)
|
68
|
+
c.connect(c.url, {:handler => send_handler}).open_sender({:name => "testlink"})
|
69
|
+
c.run
|
70
|
+
|
71
|
+
assert send_handler.accepted
|
72
|
+
assert_equal "testlink", receive_handler.link.name
|
73
|
+
assert_equal "foo", receive_handler.message.body
|
74
|
+
assert_equal "test_simple", receive_handler.link.connection.container_id
|
75
|
+
end
|
76
|
+
|
77
|
+
class CloseOnOpenHandler < TestHandler
|
78
|
+
def on_connection_open(c) super; c.close; end
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_auto_stop_one
|
82
|
+
# A listener and a connection
|
83
|
+
start_stop_handler = Class.new do
|
84
|
+
def on_container_start(c) @start = c; end
|
85
|
+
def on_container_stop(c) @stop = c; end
|
86
|
+
attr_reader :start, :stop
|
87
|
+
end.new
|
88
|
+
c = Container.new(start_stop_handler, __method__)
|
89
|
+
threads = 3.times.collect { Thread.new { c.run } }
|
90
|
+
sleep(0.01) while c.running < 3
|
91
|
+
assert_equal c, start_stop_handler.start
|
92
|
+
l = c.listen_io(TCPServer.new(0), ListenOnceHandler.new({ :handler => CloseOnOpenHandler.new}))
|
93
|
+
c.connect("amqp://:#{l.to_io.addr[1]}", { :handler => CloseOnOpenHandler.new} )
|
94
|
+
threads.each { |t| assert t.join(1) }
|
95
|
+
assert_equal c, start_stop_handler.stop
|
96
|
+
assert_raises(Container::StoppedError) { c.run }
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_auto_stop_two
|
100
|
+
# Connect between different containers
|
101
|
+
c1, c2 = Container.new("#{__method__}-1"), Container.new("#{__method__}-2")
|
102
|
+
threads = [ Thread.new {c1.run }, Thread.new {c2.run } ]
|
103
|
+
l = c2.listen_io(TCPServer.new(0), ListenOnceHandler.new({ :handler => CloseOnOpenHandler.new}))
|
104
|
+
c1.connect(l.url, { :handler => CloseOnOpenHandler.new} )
|
105
|
+
assert threads.each { |t| t.join(1) }
|
106
|
+
assert_raises(Container::StoppedError) { c1.run }
|
107
|
+
assert_raises(Container::StoppedError) { c2.connect("") }
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_auto_stop_listener_only
|
111
|
+
c = Container.new(__method__)
|
112
|
+
# Listener only, external close
|
113
|
+
t = Thread.new { c.run }
|
114
|
+
l = c.listen_io(TCPServer.new(0))
|
115
|
+
l.close
|
116
|
+
assert t.join(1)
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_stop_empty
|
120
|
+
c = Container.new(__method__)
|
121
|
+
threads = 3.times.collect { Thread.new { c.run } }
|
122
|
+
sleep(0.01) while c.running < 3
|
123
|
+
assert_nil threads[0].join(0.001) # Not stopped
|
124
|
+
c.stop
|
125
|
+
assert c.stopped
|
126
|
+
assert_raises(Container::StoppedError) { c.connect("") }
|
127
|
+
assert_raises(Container::StoppedError) { c.run }
|
128
|
+
threads.each { |t| assert t.join(1) }
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_stop
|
132
|
+
c = Container.new(__method__)
|
133
|
+
c.auto_stop = false
|
134
|
+
|
135
|
+
l = c.listen_io(TCPServer.new(0))
|
136
|
+
threads = 3.times.collect { Thread.new { c.run } }
|
137
|
+
sleep(0.01) while c.running < 3
|
138
|
+
l.close
|
139
|
+
assert_nil threads[0].join(0.001) # Not stopped, no auto_stop
|
140
|
+
|
141
|
+
l = c.listen_io(TCPServer.new(0)) # New listener
|
142
|
+
conn = c.connect("amqp://:#{l.to_io.addr[1]}")
|
143
|
+
c.stop
|
144
|
+
assert c.stopped
|
145
|
+
threads.each { |t| assert t.join(1) }
|
146
|
+
|
147
|
+
assert_raises(Container::StoppedError) { c.run }
|
148
|
+
assert_equal 0, c.running
|
149
|
+
assert_nil l.condition
|
150
|
+
assert_nil conn.condition
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_bad_host
|
154
|
+
cont = Container.new(__method__)
|
155
|
+
assert_raises (SocketError) { cont.listen("badlisten.example.com:999") }
|
156
|
+
assert_raises (SocketError) { c = cont.connect("badconnect.example.com:999") }
|
157
|
+
end
|
158
|
+
|
159
|
+
# Verify that connection options are sent to the peer and available as Connection methods
|
160
|
+
def test_connection_options
|
161
|
+
# Note: user, password and sasl_xxx options are tested by ContainerSASLTest below
|
162
|
+
server_handler = Class.new(MessagingHandler) do
|
163
|
+
def on_error(e) raise e.inspect; end
|
164
|
+
def on_connection_open(c)
|
165
|
+
@connection = c
|
166
|
+
c.open({
|
167
|
+
:virtual_host => "server.to.client",
|
168
|
+
:properties => { :server => :client },
|
169
|
+
:offered_capabilities => [ :s1 ],
|
170
|
+
:desired_capabilities => [ :s2 ],
|
171
|
+
:container_id => "box",
|
172
|
+
})
|
173
|
+
c.close
|
174
|
+
end
|
175
|
+
attr_reader :connection
|
176
|
+
end.new
|
177
|
+
# Transport options must be provided to the listener, by Connection#open it is too late
|
178
|
+
cont = TestContainer.new(nil, {
|
179
|
+
:handler => server_handler,
|
180
|
+
:idle_timeout => 88,
|
181
|
+
:max_sessions =>1000,
|
182
|
+
:max_frame_size => 8888,
|
183
|
+
})
|
184
|
+
client = cont.connect(cont.url,
|
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
|
+
cont.run
|
195
|
+
c = server_handler.connection
|
196
|
+
assert_equal "client.to.server", c.virtual_host
|
197
|
+
assert_equal({ :foo => :bar, :str => "str" }, c.properties)
|
198
|
+
assert_equal([:c1], c.offered_capabilities)
|
199
|
+
assert_equal([:c2], c.desired_capabilities)
|
200
|
+
assert_equal 21, c.idle_timeout # Proton divides by 2
|
201
|
+
assert_equal 100, c.max_sessions
|
202
|
+
assert_equal 4096, c.max_frame_size
|
203
|
+
assert_equal "bowl", c.container_id
|
204
|
+
|
205
|
+
c = client
|
206
|
+
assert_equal "server.to.client", c.virtual_host
|
207
|
+
assert_equal({ :server => :client }, c.properties)
|
208
|
+
assert_equal([:s1], c.offered_capabilities)
|
209
|
+
assert_equal([:s2], c.desired_capabilities)
|
210
|
+
assert_equal "box", c.container_id
|
211
|
+
assert_equal 8888, c.max_frame_size
|
212
|
+
assert_equal 44, c.idle_timeout # Proton divides by 2
|
213
|
+
assert_equal 100, c.max_sessions
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
class ContainerSASLTest < MiniTest::Test
|
219
|
+
include Qpid::Proton
|
220
|
+
|
221
|
+
# Handler for test client/server that sets up server and client SASL options
|
222
|
+
class SASLHandler < TestHandler
|
223
|
+
|
224
|
+
def initialize(url="amqp://", opts=nil)
|
225
|
+
super()
|
226
|
+
@url, @opts = url, opts
|
227
|
+
end
|
228
|
+
|
229
|
+
def on_container_start(container)
|
230
|
+
@client = container.connect("#{@url}:#{container.port}", @opts)
|
231
|
+
end
|
232
|
+
|
233
|
+
attr_reader :auth_user
|
234
|
+
|
235
|
+
def on_connection_open(connection)
|
236
|
+
super
|
237
|
+
if connection == @client
|
238
|
+
connection.close
|
239
|
+
else
|
240
|
+
@auth_user = connection.user
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# Generate SASL server configuration files and database, initialize proton SASL
|
246
|
+
class SASLConfig
|
247
|
+
include Qpid::Proton
|
248
|
+
attr_reader :conf_dir, :conf_file, :conf_name, :database
|
249
|
+
|
250
|
+
def initialize()
|
251
|
+
if SASL.extended? # Configure cyrus SASL
|
252
|
+
@conf_dir = File.expand_path('sasl_conf')
|
253
|
+
@conf_name = "proton-server"
|
254
|
+
@database = File.join(@conf_dir, "proton.sasldb")
|
255
|
+
@conf_file = File.join(conf_dir,"#{@conf_name}.conf")
|
256
|
+
Dir::mkdir(@conf_dir) unless File.directory?(@conf_dir)
|
257
|
+
# Same user name in different realms
|
258
|
+
make_user("user", "password", "proton") # proton realm
|
259
|
+
make_user("user", "default_password") # Default realm
|
260
|
+
File.open(@conf_file, 'w') do |f|
|
261
|
+
f.write("
|
262
|
+
sasldb_path: #{database}
|
263
|
+
mech_list: EXTERNAL DIGEST-MD5 SCRAM-SHA-1 CRAM-MD5 PLAIN ANONYMOUS
|
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
|
271
|
+
|
272
|
+
private
|
273
|
+
|
274
|
+
SASLPASSWD = (ENV['SASLPASSWD'] or 'saslpasswd2')
|
275
|
+
|
276
|
+
def make_user(user, password, realm=nil)
|
277
|
+
realm_opt = (realm ? "-u #{realm}" : "")
|
278
|
+
cmd = "echo '#{password}' | #{SASLPASSWD} -c -p -f #{database} #{realm_opt} #{user}"
|
279
|
+
system(cmd) or raise RuntimeError.new("saslpasswd2 failed: #{makepw_cmd}")
|
280
|
+
end
|
281
|
+
DEFAULT = SASLConfig.new
|
282
|
+
end
|
283
|
+
|
284
|
+
def test_sasl_anonymous()
|
285
|
+
s = SASLHandler.new("amqp://", {:sasl_allowed_mechs => "ANONYMOUS"})
|
286
|
+
TestContainer.new(s, {:sasl_allowed_mechs => "ANONYMOUS"}, __method__).run
|
287
|
+
assert_equal "anonymous", s.connections[0].user
|
288
|
+
end
|
289
|
+
|
290
|
+
def test_sasl_plain_url()
|
291
|
+
skip unless SASL.extended?
|
292
|
+
# Use default realm with URL, should authenticate with "default_password"
|
293
|
+
opts = {:sasl_allowed_mechs => "PLAIN", :sasl_allow_insecure_mechs => true}
|
294
|
+
s = SASLHandler.new("amqp://user:default_password@", opts)
|
295
|
+
TestContainer.new(s, opts, __method__).run
|
296
|
+
assert_equal(2, s.connections.size)
|
297
|
+
assert_equal("user", s.auth_user)
|
298
|
+
end
|
299
|
+
|
300
|
+
def test_sasl_plain_options()
|
301
|
+
skip unless SASL.extended?
|
302
|
+
# Use default realm with connection options, should authenticate with "default_password"
|
303
|
+
opts = {:sasl_allowed_mechs => "PLAIN",:sasl_allow_insecure_mechs => true,
|
304
|
+
:user => 'user', :password => 'default_password' }
|
305
|
+
s = SASLHandler.new("amqp://", opts)
|
306
|
+
TestContainer.new(s, {:sasl_allowed_mechs => "PLAIN",:sasl_allow_insecure_mechs => true}, __method__).run
|
307
|
+
assert_equal(2, s.connections.size)
|
308
|
+
assert_equal("user", s.auth_user)
|
309
|
+
end
|
310
|
+
|
311
|
+
# Ensure we don't allow PLAIN if allow_insecure_mechs = true is not explicitly set
|
312
|
+
def test_disallow_insecure()
|
313
|
+
# Don't set allow_insecure_mechs, but try to use PLAIN
|
314
|
+
s = SASLHandler.new("amqp://user:password@", {:sasl_allowed_mechs => "PLAIN", :sasl_allow_insecure_mechs => true})
|
315
|
+
e = assert_raises(TestError) { TestContainer.new(s, {:sasl_allowed_mechs => "PLAIN"}, __method__).run }
|
316
|
+
assert_match(/amqp:unauthorized-access.*Authentication failed/, e.to_s)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
data/tests/test_data.rb
ADDED
@@ -0,0 +1,66 @@
|
|
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 "securerandom"
|
21
|
+
require 'qpid_proton'
|
22
|
+
|
23
|
+
class TestData < MiniTest::Test
|
24
|
+
include Qpid::Proton
|
25
|
+
|
26
|
+
def assert_from_to(*values)
|
27
|
+
d = Codec::Data.new
|
28
|
+
values.each do |x|
|
29
|
+
Codec::Data.from_object(d.impl, x)
|
30
|
+
assert_equal x, Codec::Data.to_object(d.impl)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_from_to
|
35
|
+
assert_from_to({ 1 => :one, 2=>:two })
|
36
|
+
assert_from_to([{:a => 1, "b" => 2}, 3, 4.4, :five])
|
37
|
+
assert_from_to(Types::UniformArray.new(Types::INT, [1, 2, 3, 4]))
|
38
|
+
end
|
39
|
+
|
40
|
+
def rnum(*arg) SecureRandom.random_number(*arg); end
|
41
|
+
def rstr(*arg) SecureRandom.base64(*arg); end
|
42
|
+
|
43
|
+
def test_nil()
|
44
|
+
assert_nil((Codec::Data.new << nil).object)
|
45
|
+
end
|
46
|
+
|
47
|
+
def assert_convert(*values)
|
48
|
+
values.each { |x| assert_equal x, ((Codec::Data.new << x).object) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_bool()
|
52
|
+
assert_convert(true, false)
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_float()
|
56
|
+
assert_convert(0.0, 1.0, -1.0, 1.23e123, rnum(), rnum(), rnum(), rnum())
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_string()
|
60
|
+
assert_convert("", "foo", rstr(100000), rstr(rnum(1000)), rstr(rnum(1000)))
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_symbol()
|
64
|
+
assert_convert(:"", :foo, rstr(256).to_sym)
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,110 @@
|
|
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 'minitest/autorun'
|
20
|
+
require 'qpid_proton'
|
21
|
+
require 'test_tools'
|
22
|
+
include Qpid::Proton
|
23
|
+
|
24
|
+
# Test Delivery and Tracker
|
25
|
+
class TestDelivery < MiniTest::Test
|
26
|
+
|
27
|
+
class NoAutoHandler < MessagingHandler
|
28
|
+
def on_sender_open(l) l.open({:auto_settle=>false, :auto_accept=>false}); end
|
29
|
+
def on_receiver_open(l) l.open({:auto_settle=>false, :auto_accept=>false}); end
|
30
|
+
end
|
31
|
+
|
32
|
+
class SendHandler < NoAutoHandler
|
33
|
+
def initialize(to_send)
|
34
|
+
@unsent = to_send
|
35
|
+
end
|
36
|
+
|
37
|
+
def on_connection_open(connection)
|
38
|
+
@outcomes = []
|
39
|
+
@sender = connection.open_sender("x")
|
40
|
+
@unsettled = {} # Awaiting remote settlement
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_reader :outcomes, :unsent, :unsettled
|
44
|
+
|
45
|
+
def on_sendable(sender)
|
46
|
+
return if @unsent.empty?
|
47
|
+
m = Message.new(@unsent.shift)
|
48
|
+
tracker = sender.send(m)
|
49
|
+
@unsettled[tracker] = m
|
50
|
+
end
|
51
|
+
|
52
|
+
def outcome(method, tracker)
|
53
|
+
t = tracker
|
54
|
+
m = @unsettled.delete(t)
|
55
|
+
@outcomes << [m.body, method, t.id, t.state, t.modifications]
|
56
|
+
tracker.connection.close if @unsettled.empty?
|
57
|
+
end
|
58
|
+
|
59
|
+
def on_tracker_accept(tracker) outcome(__method__, tracker); end
|
60
|
+
def on_tracker_reject(tracker) outcome(__method__, tracker); end
|
61
|
+
def on_tracker_release(tracker) outcome(__method__, tracker); end
|
62
|
+
def on_tracker_modify(tracker) outcome(__method__, tracker); end
|
63
|
+
end
|
64
|
+
|
65
|
+
class ReceiveHandler < NoAutoHandler
|
66
|
+
def initialize
|
67
|
+
@received = []
|
68
|
+
end
|
69
|
+
|
70
|
+
attr_reader :received
|
71
|
+
|
72
|
+
def on_message(delivery, message)
|
73
|
+
@received << message.body
|
74
|
+
case message.body
|
75
|
+
when "accept" then delivery.accept
|
76
|
+
when "reject" then delivery.reject
|
77
|
+
when "release-really" then delivery.release({:failed=>false}) # AMQP RELEASED
|
78
|
+
when "release" then delivery.release # AMQP MODIFIED{ :failed => true }
|
79
|
+
when "modify" then delivery.release({:undeliverable => true, :annotations => {:x => 42 }})
|
80
|
+
when "modify-empty" then delivery.release({:failed => false, :undeliverable => false, :annotations => {}})
|
81
|
+
when "modify-nil" then delivery.release({:failed => false, :undeliverable => false, :annotations => nil})
|
82
|
+
when "reject-raise" then raise Reject
|
83
|
+
when "release-raise" then raise Release
|
84
|
+
else raise inspect
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_outcomes
|
90
|
+
rh = ReceiveHandler.new
|
91
|
+
sh = SendHandler.new(["accept", "reject", "release-really", "release", "modify", "modify-empty", "modify-nil", "reject-raise", "release-raise"])
|
92
|
+
c = Container.new(nil, __method__)
|
93
|
+
l = c.listen_io(TCPServer.new(0), ListenOnceHandler.new({ :handler => rh }))
|
94
|
+
c.connect(l.url, {:handler => sh})
|
95
|
+
c.run
|
96
|
+
o = sh.outcomes
|
97
|
+
assert_equal ["accept", :on_tracker_accept, "1", Transfer::ACCEPTED, nil], o.shift
|
98
|
+
assert_equal ["reject", :on_tracker_reject, "2", Transfer::REJECTED, nil], o.shift
|
99
|
+
assert_equal ["release-really", :on_tracker_release, "3", Transfer::RELEASED, nil], o.shift
|
100
|
+
assert_equal ["release", :on_tracker_modify, "4", Transfer::MODIFIED, {:failed=>true, :undeliverable=>false, :annotations=>nil}], o.shift
|
101
|
+
assert_equal ["modify", :on_tracker_modify, "5", Transfer::MODIFIED, {:failed=>true, :undeliverable=>true, :annotations=>{:x => 42}}], o.shift
|
102
|
+
assert_equal ["modify-empty", :on_tracker_release, "6", Transfer::RELEASED, nil], o.shift
|
103
|
+
assert_equal ["modify-nil", :on_tracker_release, "7", Transfer::RELEASED, nil], o.shift
|
104
|
+
assert_equal ["reject-raise", :on_tracker_reject, "8", Transfer::REJECTED, nil], o.shift
|
105
|
+
assert_equal ["release-raise", :on_tracker_modify, "9", Transfer::MODIFIED, {:failed=>true, :undeliverable=>false, :annotations=>nil}], o.shift
|
106
|
+
assert_empty o
|
107
|
+
assert_equal ["accept", "reject", "release-really", "release", "modify", "modify-empty", "modify-nil", "reject-raise", "release-raise"], rh.received
|
108
|
+
assert_empty sh.unsettled
|
109
|
+
end
|
110
|
+
end
|