Skip to content

Commit

Permalink
Merge pull request #386 from rabbitmq/rabbitmq-java-client-385
Browse files Browse the repository at this point in the history
Make sure Address.parseAddress can handle quoted IPv6 addresses
  • Loading branch information
michaelklishin authored Aug 2, 2018
2 parents e8300ab + d282928 commit d373d45
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 7 deletions.
101 changes: 94 additions & 7 deletions src/main/java/com/rabbitmq/client/Address.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,28 @@

package com.rabbitmq.client;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A representation of network addresses, i.e. host/port pairs,
* with some utility functions for parsing address strings.
*/
public class Address {
/** host name **/
private static final Logger LOGGER = LoggerFactory.getLogger(Address.class);

/**
* host name
**/
private final String _host;
/** port number **/
/**
* port number
**/
private final int _port;

/**
* Construct an address from a host name and port number.
*
* @param host the host name
* @param port the port number
*/
Expand All @@ -38,6 +48,7 @@ public Address(String host, int port) {

/**
* Construct an address from a host.
*
* @param host the host name
*/
public Address(String host) {
Expand All @@ -47,6 +58,7 @@ public Address(String host) {

/**
* Get the host name
*
* @return the host name
*/
public String getHost() {
Expand All @@ -55,23 +67,98 @@ public String getHost() {

/**
* Get the port number
*
* @return the port number
*/
public int getPort() {
return _port;
}

/**
* Extracts hostname or IP address from a string containing a hostname, IP address,
* hostname:port pair or IP address:port pair.
* Note that IPv6 addresses must be quoted with square brackets, e.g. [2001:db8:85a3:8d3:1319:8a2e:370:7348].
*
* @param addressString the string to extract hostname from
* @return the hostname or IP address
*/
public static String parseHost(String addressString) {
// we need to handle cases such as [2001:db8:85a3:8d3:1319:8a2e:370:7348]:5671
int lastColon = addressString.lastIndexOf(":");
int lastClosingSquareBracket = addressString.lastIndexOf("]");
if (lastClosingSquareBracket == -1) {
String[] parts = addressString.split(":");
if (parts.length > 2) {
String msg = "Address " +
addressString +
" seems to contain an unquoted IPv6 address. Make sure you quote IPv6 addresses like so: [2001:db8:85a3:8d3:1319:8a2e:370:7348]";
LOGGER.error(msg);
throw new IllegalArgumentException(msg);
}

return parts[0];
}

if (lastClosingSquareBracket < lastColon) {
// there is a port
return addressString.substring(0, lastColon);
} else {
return addressString;
}
}

public static int parsePort(String addressString) {
// we need to handle cases such as [2001:db8:85a3:8d3:1319:8a2e:370:7348]:5671
int lastColon = addressString.lastIndexOf(":");
int lastClosingSquareBracket = addressString.lastIndexOf("]");
if (lastClosingSquareBracket == -1) {
String[] parts = addressString.split(":");
if (parts.length > 2) {
String msg = "Address " +
addressString +
" seems to contain an unquoted IPv6 address. Make sure you quote IPv6 addresses like so: [2001:db8:85a3:8d3:1319:8a2e:370:7348]";
LOGGER.error(msg);
throw new IllegalArgumentException(msg);
}

if (parts.length == 2) {
return Integer.parseInt(parts[1]);
}

return ConnectionFactory.USE_DEFAULT_PORT;
}

if (lastClosingSquareBracket < lastColon) {
// there is a port
return Integer.parseInt(addressString.substring(lastColon + 1));
}

return ConnectionFactory.USE_DEFAULT_PORT;
}

public static boolean isHostWithPort(String addressString) {
// we need to handle cases such as [2001:db8:85a3:8d3:1319:8a2e:370:7348]:5671
int lastColon = addressString.lastIndexOf(":");
int lastClosingSquareBracket = addressString.lastIndexOf("]");

if (lastClosingSquareBracket == -1) {
return addressString.contains(":");
} else {
return lastClosingSquareBracket < lastColon;
}
}

/**
* Factory method: takes a formatted addressString string as construction parameter
* @param addressString an addressString of the form "host[:port]".
* @return an {@link Address} from the given data
*/
public static Address parseAddress(String addressString) {
int idx = addressString.indexOf(':');
return (idx == -1) ?
new Address(addressString) :
new Address(addressString.substring(0, idx),
Integer.parseInt(addressString.substring(idx+1)));
if (isHostWithPort(addressString)) {
return new Address(parseHost(addressString), parsePort(addressString));
} else {
return new Address(addressString);
}
}

/**
Expand Down
90 changes: 90 additions & 0 deletions src/test/java/com/rabbitmq/client/test/AddressTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) 2018 Pivotal Software, Inc. All rights reserved.
//
// This software, the RabbitMQ Java client library, is triple-licensed under the
// Mozilla Public License 1.1 ("MPL"), the GNU General Public License version 2
// ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see
// LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL,
// please see LICENSE-APACHE2.
//
// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
// either express or implied. See the LICENSE file for specific language governing
// rights and limitations of this software.
//
// If you have any questions regarding licensing, please contact us at
// info@rabbitmq.com.

package com.rabbitmq.client.test;

import com.rabbitmq.client.Address;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

/**
*
*/
public class AddressTest {

@Test public void isHostWithPort() {
assertTrue(Address.isHostWithPort("127.0.0.1:5672"));
assertTrue(Address.isHostWithPort("[1080:0:0:0:8:800:200C:417A]:5672"));
assertTrue(Address.isHostWithPort("[::1]:5672"));

assertFalse(Address.isHostWithPort("127.0.0.1"));
assertFalse(Address.isHostWithPort("[1080:0:0:0:8:800:200C:417A]"));
assertFalse(Address.isHostWithPort("[::1]"));
}

@Test public void parseHost() {
assertEquals("127.0.0.1", Address.parseHost("127.0.0.1:5672"));
assertEquals("[1080:0:0:0:8:800:200C:417A]", Address.parseHost("[1080:0:0:0:8:800:200C:417A]:5673"));
assertEquals("[::1]", Address.parseHost("[::1]:5672"));

assertEquals("127.0.0.1", Address.parseHost("127.0.0.1"));
assertEquals("[1080:0:0:0:8:800:200C:417A]", Address.parseHost("[1080:0:0:0:8:800:200C:417A]"));
assertEquals("[::1]", Address.parseHost("[::1]"));
}

@Test public void parsePort() {
assertEquals(5672, Address.parsePort("127.0.0.1:5672"));
assertEquals(5673, Address.parsePort("[1080:0:0:0:8:800:200C:417A]:5673"));
assertEquals(5672, Address.parsePort("[::1]:5672"));

// "use default port" value
assertEquals(-1, Address.parsePort("127.0.0.1"));
assertEquals(-1, Address.parsePort("[1080:0:0:0:8:800:200C:417A]"));
assertEquals(-1, Address.parsePort("[::1]"));
}

@Test public void parseIPv4() {
assertEquals(addr("192.168.1.10"), Address.parseAddress("192.168.1.10"));
assertEquals(addr("192.168.1.10", 5682), Address.parseAddress("192.168.1.10:5682"));
}

@Test public void parseIPv6() {
// quoted IPv6 addresses without a port
assertEquals(addr("[1080:0:0:0:8:800:200C:417A]"), Address.parseAddress("[1080:0:0:0:8:800:200C:417A]"));
assertEquals(addr("[::1]"), Address.parseAddress("[::1]"));

// quoted IPv6 addresses with a port
assertEquals(addr("[1080:0:0:0:8:800:200C:417A]", 5673), Address.parseAddress("[1080:0:0:0:8:800:200C:417A]:5673"));
assertEquals(addr("[::1]", 5673), Address.parseAddress("[::1]:5673"));
}

@Test(expected = IllegalArgumentException.class)
public void parseUnquotedIPv6() {
// using a non-quoted IPv6 addresses with a port
Address.parseAddress("::1:5673");
}

private Address addr(String addr) {
return new Address(addr);
}

private Address addr(String addr, int port) {
return new Address(addr, port);
}

}

0 comments on commit d373d45

Please sign in to comment.