Skip to content

Commit 3d1cdcd

Browse files
author
Alexandru Scvortov
committed
add AMQP URI parsing to ConnectionFactory and tests
Of course, escapes in the user-info part of the URI confuse Java's parser, so amqp://user%61:%61pass@ho%61st:10000/v%2fhost doesn't get parsed (in particular, the "user%61:%61pass@ho%61st:10000" part isn't split into user-info, host, and port.
1 parent 58ff845 commit 3d1cdcd

File tree

3 files changed

+173
-1
lines changed

3 files changed

+173
-1
lines changed

src/com/rabbitmq/client/ConnectionFactory.java

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@
2121
import java.security.NoSuchAlgorithmException;
2222
import java.util.Map;
2323

24-
import java.net.Socket;
2524
import java.net.InetSocketAddress;
25+
import java.net.Socket;
26+
import java.net.URI;
27+
import java.net.URISyntaxException;
28+
import java.net.URLDecoder;
2629

2730
import javax.net.SocketFactory;
2831
import javax.net.ssl.SSLSocketFactory;
@@ -180,6 +183,66 @@ public void setVirtualHost(String virtualHost) {
180183
this.virtualHost = virtualHost;
181184
}
182185

186+
/**
187+
* Convenience method for setting the fields in an AMQP URI: host,
188+
* port, username, password and virtual host. If any part of the
189+
* URI is ommited, the ConnectionFactory's corresponding variable
190+
* is left unchanged.
191+
* @param uriString is the AMQP URI containing the data
192+
*/
193+
public void setUri(String uriString)
194+
throws URISyntaxException
195+
{
196+
URI uri = new URI(uriString);
197+
198+
if (!"amqp".equals(uri.getScheme())) {
199+
throw new IllegalArgumentException("Wrong scheme in AMQP URI: " + uriString);
200+
}
201+
202+
String host = uri.getHost();
203+
if (host != null) {
204+
setHost(host);
205+
}
206+
207+
int port = uri.getPort();
208+
if (port != -1) {
209+
setPort(port);
210+
}
211+
212+
String userInfo = uri.getRawUserInfo();
213+
if (userInfo != null) {
214+
String userPass[] = userInfo.split(":");
215+
if (userPass.length > 2) {
216+
throw new IllegalArgumentException("Bad user info in AMQP URI: " + uriString);
217+
}
218+
219+
setUsername(uriDecode(userPass[0]));
220+
if (userPass.length == 2) {
221+
setPassword(uriDecode(userPass[1]));
222+
}
223+
}
224+
225+
String path = uri.getRawPath();
226+
if (path != null && path.length() > 0) {
227+
if (path.indexOf('/', 1) != -1) {
228+
throw new IllegalArgumentException("Multiple segemnts in path of AMQP URI: " + uriString);
229+
}
230+
231+
setVirtualHost(uriDecode(uri.getPath().substring(1)));
232+
}
233+
}
234+
235+
private String uriDecode(String s) {
236+
try {
237+
// URLDecode decodes '+' to a space, as for
238+
// form encoding. So protect plus signs.
239+
return URLDecoder.decode(s.replace("+", "%2B"), "US-ASCII");
240+
}
241+
catch (java.io.UnsupportedEncodingException e) {
242+
throw new RuntimeException(e);
243+
}
244+
}
245+
183246
/**
184247
* Retrieve the requested maximum channel number
185248
* @return the initially requested maximum channel number; zero for unlimited
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// The contents of this file are subject to the Mozilla Public License
2+
// Version 1.1 (the "License"); you may not use this file except in
3+
// compliance with the License. You may obtain a copy of the License
4+
// at http://www.mozilla.org/MPL/
5+
//
6+
// Software distributed under the License is distributed on an "AS IS"
7+
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
8+
// the License for the specific language governing rights and
9+
// limitations under the License.
10+
//
11+
// The Original Code is RabbitMQ.
12+
//
13+
// The Initial Developer of the Original Code is VMware, Inc.
14+
// Copyright (c) 2011 VMware, Inc. All rights reserved.
15+
//
16+
//
17+
package com.rabbitmq.client.test;
18+
19+
import com.rabbitmq.client.ConnectionFactory;
20+
21+
import java.net.URISyntaxException;
22+
23+
public class AmqpUriTest extends BrokerTestCase
24+
{
25+
public void testUriParsing() throws URISyntaxException {
26+
/* From the spec */
27+
parseSuccess("amqp://user:pass@host:10000/vhost",
28+
"user", "pass", "host", 10000, "vhost");
29+
parseSuccess("amqp://user%61:%61pass@ho%61st:10000/v%2fhost",
30+
"usera", "apass", "hoast", 10000, "v/host");
31+
parseSuccess("amqp://", "guest", "guest", "localhost", 5672, "/");
32+
parseSuccess("amqp://:@/", "", "", "localhost", 5672, "");
33+
parseSuccess("amqp://user@", "user", "guest", "localhost", 5672, "/");
34+
parseSuccess("amqp://user:pass@",
35+
"user", "pass", "localhost", 5672, "/");
36+
parseSuccess("amqp://host", "guest", "guest", "host", 5672, "/");
37+
parseSuccess("amqp://:10000",
38+
"guest", "guest", "localhost", 10000, "/");
39+
parseSuccess("amqp:///vhost",
40+
"guest", "guest", "localhost", 5672, "vhost");
41+
parseSuccess("amqp://host/", "guest", "guest", "host", 5672, "");
42+
parseSuccess("amqp://host/%2f", "guest", "guest", "host", 5672, "/");
43+
parseSuccess("amqp://[::1]", "guest", "guest", "::1", 5672, "/");
44+
45+
/* Various other success cases */
46+
parseSuccess("amqp://host:100", "guest", "guest", "host", 100, "/");
47+
parseSuccess("amqp://[::1]:100", "guest", "guest", "::1", 100, "/");
48+
49+
parseSuccess("amqp://host/blah",
50+
"guest", "guest", "host", 5672, "blah");
51+
parseSuccess("amqp://host:100/blah",
52+
"guest", "guest", "host", 100, "blah");
53+
parseSuccess("amqp://:100/blah", "guest", "guest",
54+
"localhost", 100, "blah");
55+
parseSuccess("amqp://[::1]/blah",
56+
"guest", "guest", "::1", 5672, "blah");
57+
parseSuccess("amqp://[::1]:100/blah",
58+
"guest", "guest", "::1", 100, "blah");
59+
60+
parseSuccess("amqp://user:pass@host",
61+
"user", "pass", "host", 5672, "/");
62+
parseSuccess("amqp://user:pass@host:100",
63+
"user", "pass", "host", 100, "/");
64+
parseSuccess("amqp://user:pass@:100",
65+
"user", "pass", "localhost", 100, "/");
66+
parseSuccess("amqp://user:pass@[::1]",
67+
"user", "pass", "::1", 5672, "/");
68+
parseSuccess("amqp://user:pass@[::1]:100",
69+
"user", "pass", "::1", 100, "/");
70+
71+
/* Various failure cases */
72+
parseFail("http://www.rabbitmq.com");
73+
parseFail("amqp://foo:bar:baz");
74+
parseFail("amqp://foo[::1]");
75+
parseFail("amqp://foo:[::1]");
76+
parseFail("amqp://[::1]foo");
77+
parseFail("amqp://foo:1000xyz");
78+
parseFail("amqp://foo:1000000");
79+
parseFail("amqp://foo/bar/baz");
80+
81+
parseFail("amqp://foo%1");
82+
parseFail("amqp://foo%1x");
83+
parseFail("amqp://foo%xy");
84+
}
85+
86+
private void parseSuccess(String uri, String user, String password,
87+
String host, int port, String vhost)
88+
throws URISyntaxException
89+
{
90+
ConnectionFactory cf = new ConnectionFactory();
91+
cf.setUri(uri);
92+
93+
assertEquals(user, cf.getUsername());
94+
assertEquals(password, cf.getPassword());
95+
assertEquals(host, cf.getHost());
96+
assertEquals(port, cf.getPort());
97+
assertEquals(vhost, cf.getVirtualHost());
98+
}
99+
100+
private void parseFail(String uri) {
101+
try {
102+
(new ConnectionFactory()).setUri(uri);
103+
fail("URI parse didn't fail: '" + uri + "'");
104+
} catch (Exception e) {
105+
// whoosh!
106+
}
107+
}
108+
}

test/src/com/rabbitmq/client/test/ClientTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public static TestSuite suite() {
3636
suite.addTestSuite(QueueingConsumerShutdownTests.class);
3737
suite.addTestSuite(com.rabbitmq.utility.IntAllocatorTests.class);
3838
suite.addTestSuite(AMQBuilderApiTest.class);
39+
suite.addTestSuite(AmqpUriTest.class);
3940
return suite;
4041
}
4142
}

0 commit comments

Comments
 (0)