qpid_proton 0.19.0 → 0.21.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 +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
|