add support for ipv6 hostnames

This commit is contained in:
Xavier Léauté 2014-12-01 12:16:28 -08:00
parent d23fd1e1ab
commit eb5525f9b4
2 changed files with 101 additions and 33 deletions

View File

@ -29,6 +29,8 @@ import io.druid.common.utils.SocketUtil;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.net.URI;
import java.net.URISyntaxException;
/**
*/
@ -48,6 +50,22 @@ public class DruidNode
@Min(0) @Max(0xffff)
private int port = -1;
/**
* host = null , port = null -> host = _default_, port = -1
* host = "abc:123", port = null -> host = abc, port = 123
* host = "abc:fff", port = null -> throw IAE (invalid ipv6 host)
* host = "2001:db8:85a3::8a2e:370:7334", port = null -> host = [2001:db8:85a3::8a2e:370:7334], port = _auto_
* host = "[2001:db8:85a3::8a2e:370:7334]", port = null -> host = [2001:db8:85a3::8a2e:370:7334], port = _auto_
* host = "abc" , port = null -> host = abc, port = _auto_
* host = "abc" , port = 123 -> host = abc, port = 123
* host = "abc:123 , port = 123 -> host = abc, port = 123
* host = "abc:123 , port = 456 -> throw IAE (conflicting port)
* host = "abc:fff , port = 456 -> throw IAE (invalid ipv6 host)
* host = "[2001:db8:85a3::8a2e:370:7334]:123", port = null -> host = [2001:db8:85a3::8a2e:370:7334], port = 123
* host = "[2001:db8:85a3::8a2e:370:7334]", port = 123 -> host = [2001:db8:85a3::8a2e:370:7334], port = 123
* host = "2001:db8:85a3::8a2e:370:7334", port = 123 -> host = [2001:db8:85a3::8a2e:370:7334], port = 123
* host = null , port = 123 -> host = _default_, port = 123
*/
@JsonCreator
public DruidNode(
@JacksonInject @Named("serviceName") @JsonProperty("service") String serviceName,
@ -58,43 +76,53 @@ public class DruidNode
init(serviceName, host, port);
}
/**
* host = "abc:123", port = null -> host = abc, port = 123
* host = "abc:fff", port = null -> host = abc, port = -1
* host = "abc" , port = null -> host = abc, port = _auto_
* host = null , port = null -> host = _default_, port = -1
* host = "abc:123 , port = 456 -> throw IAE
* host = "abc:fff , port = 456 -> throw IAE
* host = "abc:123 , port = 123 -> host = abc, port = 123
* host = "abc" , port = 123 -> host = abc, port = 123
* host = null , port = 123 -> host = _default_, port = 123
*/
private void init(String serviceName, String host, Integer port)
{
this.serviceName = serviceName;
if (host != null && host.contains(":")) {
final String[] hostParts = host.split(":");
int parsedPort = -1;
int parsedPort = -1;
if (host != null) {
try {
parsedPort = Integer.parseInt(hostParts[1]);
// try host:port parsing (necessary for IPv6)
final URI uri = new URI(null, host, null, null, null);
// host is null if authority cannot be parsed into host and port
if(uri.getHost() != null) {
parsedPort = uri.getPort();
host = uri.getHost();
} else {
throw new IllegalArgumentException();
}
}
catch (NumberFormatException e) {
// leave -1
catch (IllegalArgumentException | URISyntaxException ee) {
// try host alone
try {
final URI uri = new URI(null, host, null, null);
host = uri.getHost();
} catch(URISyntaxException e) {
throw new IllegalArgumentException(e);
}
}
if (port != null && port != parsedPort) {
if (port != null && parsedPort != -1 && port != parsedPort) {
throw new IAE("Conflicting host:port [%s] and port [%d] settings", host, port);
}
host = hostParts[0];
port = parsedPort;
}
if (port == null && host != null) {
port = SocketUtil.findOpenPort(8080);
if (port == null) {
if (parsedPort == -1 && host != null) {
port = SocketUtil.findOpenPort(8080);
} else {
port = parsedPort;
}
}
this.port = port != null ? port : -1;
this.host = host != null ? host : DEFAULT_HOST;
try {
new URI(null, null, this.host, this.port, null, null, null).getAuthority();
} catch(URISyntaxException e) {
throw new IllegalArgumentException(e);
}
}
public String getServiceName()
@ -112,8 +140,16 @@ public class DruidNode
return port;
}
/**
* Returns host and port together as something that can be used as part of a URI.
*/
public String getHostAndPort() {
return String.format("%s:%d", host, port);
try {
return new URI(null, null, host, port, null, null, null).getAuthority();
} catch(URISyntaxException e) {
// should never happen, since we tried it in init already
throw new RuntimeException(e);
}
}
@Override

View File

@ -30,32 +30,52 @@ public class DruidNodeTest
final String service = "test/service";
DruidNode node;
node = new DruidNode(service, null, null);
Assert.assertEquals(DruidNode.DEFAULT_HOST, node.getHost());
Assert.assertEquals(-1, node.getPort());
node = new DruidNode(service, "abc:123", null);
Assert.assertEquals("abc", node.getHost());
Assert.assertEquals(123, node.getPort());
Assert.assertEquals("abc:123", node.getHostAndPort());
node = new DruidNode(service, "abc:fff", null);
Assert.assertEquals("abc", node.getHost());
Assert.assertEquals(-1, node.getPort());
node = new DruidNode(service, "2001:db8:85a3::8a2e:370:7334", null);
Assert.assertEquals("[2001:db8:85a3::8a2e:370:7334]", node.getHost());
Assert.assertTrue(8080 <= node.getPort());
node = new DruidNode(service, "[2001:db8:85a3::8a2e:370:7334]", null);
Assert.assertEquals("[2001:db8:85a3::8a2e:370:7334]", node.getHost());
Assert.assertTrue(8080 <= node.getPort());
node = new DruidNode(service, "abc", null);
Assert.assertEquals("abc", node.getHost());
Assert.assertTrue(8080 <= node.getPort());
node = new DruidNode(service, null, null);
Assert.assertEquals(DruidNode.DEFAULT_HOST, node.getHost());
Assert.assertEquals(-1, node.getPort());
node = new DruidNode(service, "abc", 123);
Assert.assertEquals("abc", node.getHost());
Assert.assertEquals(123, node.getPort());
Assert.assertEquals("abc:123", node.getHostAndPort());
node = new DruidNode(service, "abc:123", 123);
Assert.assertEquals("abc", node.getHost());
Assert.assertEquals(123, node.getPort());
Assert.assertEquals("abc:123", node.getHostAndPort());
node = new DruidNode(service, "abc", 123);
Assert.assertEquals("abc", node.getHost());
node = new DruidNode(service, "[2001:db8:85a3::8a2e:370:7334]:123", null);
Assert.assertEquals("[2001:db8:85a3::8a2e:370:7334]", node.getHost());
Assert.assertEquals(123, node.getPort());
Assert.assertEquals("abc:123", node.getHostAndPort());
Assert.assertEquals("[2001:db8:85a3::8a2e:370:7334]:123", node.getHostAndPort());
node = new DruidNode(service, "2001:db8:85a3::8a2e:370:7334", 123);
Assert.assertEquals("[2001:db8:85a3::8a2e:370:7334]", node.getHost());
Assert.assertEquals(123, node.getPort());
Assert.assertEquals("[2001:db8:85a3::8a2e:370:7334]:123", node.getHostAndPort());
node = new DruidNode(service, "[2001:db8:85a3::8a2e:370:7334]", 123);
Assert.assertEquals("[2001:db8:85a3::8a2e:370:7334]", node.getHost());
Assert.assertEquals(123, node.getPort());
Assert.assertEquals("[2001:db8:85a3::8a2e:370:7334]:123", node.getHostAndPort());
node = new DruidNode(service, null, 123);
Assert.assertEquals(DruidNode.DEFAULT_HOST, node.getHost());
@ -69,8 +89,20 @@ public class DruidNodeTest
}
@Test(expected = IllegalArgumentException.class)
public void testConflictingPortsNonsense() throws Exception
public void testInvalidIPv6WithPort() throws Exception
{
new DruidNode("test/service", "[abc:fff]:123", 456);
}
@Test(expected = IllegalArgumentException.class)
public void testInvalidIPv6() throws Exception
{
new DruidNode("test/service", "abc:fff", 456);
}
@Test(expected = IllegalArgumentException.class)
public void testConflictingPortsNonsense() throws Exception
{
new DruidNode("test/service", "[2001:db8:85a3::8a2e:370:7334]:123", 456);
}
}