You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2020/12/28 10:55:59 UTC
[ignite] branch master updated: IGNITE-13555 Java thin: add IPv6
address support
This is an automated email from the ASF dual-hosted git repository.
ptupitsyn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 64dbfcf IGNITE-13555 Java thin: add IPv6 address support
64dbfcf is described below
commit 64dbfcf574cdf0beb635eaa900daaca22d5a5185
Author: Varvara Kozhukhova <53...@users.noreply.github.com>
AuthorDate: Mon Dec 28 13:55:30 2020 +0300
IGNITE-13555 Java thin: add IPv6 address support
- Change HostAndPortRange.parse method to support addresses like [IPv6_host]:port1..port2, because previous implementation didn't recognized IPv6.
- Add tests for HostAndPortRange.parse method for both IPv4 and IPv6 hosts.
---
.../processors/odbc/ClientListenerProcessor.java | 2 +-
.../ignite/internal/util/HostAndPortRange.java | 133 +++++++++++----
.../org/apache/ignite/client/ConnectionTest.java | 31 +++-
.../apache/ignite/client/LocalIgniteCluster.java | 14 +-
.../ignite/internal/util/HostAndPortRangeTest.java | 181 +++++++++++++++++++++
.../ignite/testsuites/IgniteUtilSelfTestSuite.java | 5 +-
6 files changed, 317 insertions(+), 49 deletions(-)
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerProcessor.java
index 517d213..4a51fb0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerProcessor.java
@@ -201,7 +201,7 @@ public class ClientListenerProcessor extends GridProcessorAdapter {
if (lastErr != null)
throw new IgniteCheckedException("Failed to bind to any [host:port] from the range [" +
"host=" + host + ", portFrom=" + cliConnCfg.getPort() + ", portTo=" + portTo +
- ", lastErr=" + lastErr + ']');
+ ", lastErr=" + lastErr + ']', lastErr);
if (!U.IGNITE_MBEANS_DISABLED)
registerMBean();
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/HostAndPortRange.java b/modules/core/src/main/java/org/apache/ignite/internal/util/HostAndPortRange.java
index 9bfca7f..0255275 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/HostAndPortRange.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/HostAndPortRange.java
@@ -18,6 +18,8 @@
package org.apache.ignite.internal.util;
import java.io.Serializable;
+import java.net.Inet6Address;
+import java.net.UnknownHostException;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.util.typedef.F;
@@ -53,60 +55,106 @@ public class HostAndPortRange implements Serializable {
String host;
+ String portStr;
int portFrom;
int portTo;
if (F.isEmpty(addrStr))
throw createParseError(addrStr, errMsgPrefix, "Address is empty");
- final int colIdx = addrStr.indexOf(':');
-
- if (colIdx > 0) {
- String portFromStr;
- String portToStr;
+ if (addrStr.charAt(0) == '[') { // IPv6 with port(s)
+ int hostEndIdx = addrStr.indexOf(']');
+
+ if (hostEndIdx == -1)
+ throw createParseError(addrStr, errMsgPrefix, "Failed to parse IPv6 address, missing ']'");
+
+ host = addrStr.substring(1, hostEndIdx);
+
+ if (hostEndIdx == addrStr.length() - 1) { // no port specified, using default
+ portFrom = dfltPortFrom;
+ portTo = dfltPortTo;
+ }
+ else { // port specified
+ portStr = addrStr.substring(hostEndIdx + 2);
- host = addrStr.substring(0, colIdx);
+ int[] ports = verifyPortStr(addrStr, errMsgPrefix, portStr);
+ portFrom = ports[0];
+ portTo = ports[1];
+ }
+ }
+ else { // IPv4 || IPv6 without port || empty host
+ final int colIdx = addrStr.lastIndexOf(':');
+
+ if (colIdx > 0) {
+ if (addrStr.lastIndexOf(':', colIdx - 1) != -1) { // IPv6 without [] and port
+ try {
+ Inet6Address.getByName(addrStr);
+ host = addrStr;
+ portFrom = dfltPortFrom;
+ portTo = dfltPortTo;
+ }
+ catch (UnknownHostException e) {
+ throw createParseError(addrStr, errMsgPrefix, "IPv6 is incorrect", e);
+ }
+ }
+ else {
+ host = addrStr.substring(0, colIdx);
+ portStr = addrStr.substring(colIdx + 1);
+ int[] ports = verifyPortStr(addrStr, errMsgPrefix, portStr);
+ portFrom = ports[0];
+ portTo = ports[1];
+ }
+ }
+ else if (colIdx == 0)
+ throw createParseError(addrStr, errMsgPrefix, "Host name is empty");
+ else { // Port is not specified, use defaults.
+ host = addrStr;
- String portStr = addrStr.substring(colIdx + 1, addrStr.length());
+ portFrom = dfltPortFrom;
+ portTo = dfltPortTo;
+ }
+ }
- if (F.isEmpty(portStr))
- throw createParseError(addrStr, errMsgPrefix, "port range is not specified");
+ return new HostAndPortRange(host, portFrom, portTo);
+ }
- int portRangeIdx = portStr.indexOf("..");
+ /**
+ * Verifies string containing single port or ports range.
+ *
+ * @param addrStr Address String.
+ * @param errMsgPrefix Error message prefix.
+ * @param portStr Port or port range string.
+ * @return Array of int[portFrom, portTo].
+ * @throws IgniteCheckedException If failed.
+ */
+ private static int[] verifyPortStr(String addrStr, String errMsgPrefix, String portStr)
+ throws IgniteCheckedException {
+ String portFromStr;
+ String portToStr;
- if (portRangeIdx >= 0) {
- // Port range is specified.
- portFromStr = portStr.substring(0, portRangeIdx);
- portToStr = portStr.substring(portRangeIdx + 2, portStr.length());
- }
- else {
- // Single port is specified.
- portFromStr = portStr;
- portToStr = portStr;
- }
+ if (F.isEmpty(portStr))
+ throw createParseError(addrStr, errMsgPrefix, "port range is not specified");
- portFrom = parsePort(portFromStr, addrStr, errMsgPrefix);
- portTo = parsePort(portToStr, addrStr, errMsgPrefix);
+ int portRangeIdx = portStr.indexOf("..");
- if (portFrom > portTo)
- throw createParseError(addrStr, errMsgPrefix, "start port cannot be less than end port");
+ if (portRangeIdx >= 0) {
+ // Port range is specified.
+ portFromStr = portStr.substring(0, portRangeIdx);
+ portToStr = portStr.substring(portRangeIdx + 2);
}
else {
- // Host name not specified.
- if (colIdx == 0)
- throw createParseError(addrStr, errMsgPrefix, "Host name is empty");
-
- // Port is not specified, use defaults.
- host = addrStr;
-
- portFrom = dfltPortFrom;
- portTo = dfltPortTo;
+ // Single port is specified.
+ portFromStr = portStr;
+ portToStr = portStr;
}
- if (F.isEmpty(host))
- throw createParseError(addrStr, errMsgPrefix, "Host name is empty");
+ int portFrom = parsePort(portFromStr, addrStr, errMsgPrefix);
+ int portTo = parsePort(portToStr, addrStr, errMsgPrefix);
- return new HostAndPortRange(host, portFrom, portTo);
+ if (portFrom > portTo)
+ throw createParseError(addrStr, errMsgPrefix, "start port cannot be less than end port");
+
+ return new int[] {portFrom, portTo};
}
/**
@@ -145,6 +193,19 @@ public class HostAndPortRange implements Serializable {
}
/**
+ * Create parse error with cause - nested exception.
+ *
+ * @param addrStr Address string.
+ * @param errMsgPrefix Error message prefix.
+ * @param errMsg Error message.
+ * @param cause Cause exception.
+ * @return Exception.
+ */
+ private static IgniteCheckedException createParseError(String addrStr, String errMsgPrefix, String errMsg, Throwable cause) {
+ return new IgniteCheckedException(errMsgPrefix + " (" + errMsg + "): " + addrStr, cause);
+ }
+
+ /**
* Constructor.
*
* @param host Host.
diff --git a/modules/core/src/test/java/org/apache/ignite/client/ConnectionTest.java b/modules/core/src/test/java/org/apache/ignite/client/ConnectionTest.java
index d591428..b394f3c 100644
--- a/modules/core/src/test/java/org/apache/ignite/client/ConnectionTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/client/ConnectionTest.java
@@ -19,53 +19,68 @@ package org.apache.ignite.client;
import org.apache.ignite.Ignition;
import org.apache.ignite.configuration.ClientConfiguration;
+import org.junit.Ignore;
import org.junit.Test;
/**
* Checks if it can connect to a valid address from the node address list.
*/
public class ConnectionTest {
+ /** IPv4 default host. */
+ public static final String IPv4_HOST = "127.0.0.1";
+
+ /** IPv6 default host. */
+ public static final String IPv6_HOST = "::1";
+
/** */
@Test(expected = org.apache.ignite.client.ClientException.class)
public void testEmptyNodeAddress() throws Exception {
- testConnection("");
+ testConnection(IPv4_HOST, "");
}
/** */
@Test(expected = org.apache.ignite.client.ClientException.class)
public void testNullNodeAddress() throws Exception {
- testConnection(null);
+ testConnection(IPv4_HOST, null);
}
/** */
@Test(expected = org.apache.ignite.client.ClientException.class)
public void testNullNodeAddresses() throws Exception {
- testConnection(null, null);
+ testConnection(IPv4_HOST, null, null);
}
/** */
@Test
public void testValidNodeAddresses() throws Exception {
- testConnection(Config.SERVER);
+ testConnection(IPv4_HOST, Config.SERVER);
}
/** */
@Test(expected = org.apache.ignite.client.ClientConnectionException.class)
public void testInvalidNodeAddresses() throws Exception {
- testConnection("127.0.0.1:47500", "127.0.0.1:10801");
+ testConnection(IPv4_HOST, "127.0.0.1:47500", "127.0.0.1:10801");
}
/** */
@Test
public void testValidInvalidNodeAddressesMix() throws Exception {
- testConnection("127.0.0.1:47500", "127.0.0.1:10801", Config.SERVER);
+ testConnection(IPv4_HOST, "127.0.0.1:47500", "127.0.0.1:10801", Config.SERVER);
+ }
+
+ /** */
+ @Ignore("IPv6 is not enabled by default on some systems.")
+ @Test
+ public void testIPv6NodeAddresses() throws Exception {
+ testConnection(IPv6_HOST, "[::1]:10800");
}
/**
* @param addrs Addresses to connect.
+ * @param host LocalIgniteCluster host.
*/
- private void testConnection(String... addrs) throws Exception {
- try (LocalIgniteCluster cluster = LocalIgniteCluster.start(1);
+ private void testConnection(String host, String... addrs) throws Exception {
+ try (LocalIgniteCluster cluster = LocalIgniteCluster.start(1, host);
IgniteClient client = Ignition.startClient(new ClientConfiguration()
.setAddresses(addrs))) {
}
diff --git a/modules/core/src/test/java/org/apache/ignite/client/LocalIgniteCluster.java b/modules/core/src/test/java/org/apache/ignite/client/LocalIgniteCluster.java
index c6e1593..50ae358 100644
--- a/modules/core/src/test/java/org/apache/ignite/client/LocalIgniteCluster.java
+++ b/modules/core/src/test/java/org/apache/ignite/client/LocalIgniteCluster.java
@@ -35,7 +35,7 @@ import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
*/
public class LocalIgniteCluster implements AutoCloseable {
/** Host. */
- private static final String HOST = "127.0.0.1";
+ private static String host = "127.0.0.1";
/** Randomizer. */
private static final Random rnd = new Random();
@@ -68,12 +68,20 @@ public class LocalIgniteCluster implements AutoCloseable {
}
/**
- * Create and start start the cluster.
+ * Create and start start the cluster with default host.
*/
public static LocalIgniteCluster start(int initSize) {
return new LocalIgniteCluster(initSize);
}
+ /**
+ * Create and start start the cluster with custom host.
+ */
+ public static LocalIgniteCluster start(int initSize, String host) {
+ LocalIgniteCluster.host = host;
+ return new LocalIgniteCluster(initSize);
+ }
+
/** {@inheritDoc} */
@Override public synchronized void close() {
srvs.forEach(Ignite::close);
@@ -155,7 +163,7 @@ public class LocalIgniteCluster implements AutoCloseable {
IgniteConfiguration igniteCfg = Config.getServerConfiguration();
igniteCfg.setClientConnectorConfiguration(new ClientConnectorConfiguration()
- .setHost(HOST)
+ .setHost(host)
.setPort(nodeCfg.getClientPort())
);
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/util/HostAndPortRangeTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/HostAndPortRangeTest.java
new file mode 100644
index 0000000..1299e32
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/util/HostAndPortRangeTest.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.util;
+
+import java.net.UnknownHostException;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.apache.ignite.testframework.junits.common.GridCommonTest;
+import org.hamcrest.core.IsInstanceOf;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+/**
+ * Tests HostAndPortRange parse method.
+ */
+@GridCommonTest(group = "Utils")
+public class HostAndPortRangeTest extends GridCommonAbstractTest {
+ /**
+ * Tests correct input address with IPv4 host and port range.
+ *
+ * @throws IgniteCheckedException on incorrect host/port
+ */
+ @Test
+ public void testParseIPv4WithPortRange() throws IgniteCheckedException {
+ String addrStr = "127.0.0.1:8080..8090";
+ String errMsgPrefix = "";
+ int dfltPortFrom = 18360;
+ int dfltPortTo = 18362;
+ HostAndPortRange actual = HostAndPortRange.parse(addrStr, dfltPortFrom, dfltPortTo, errMsgPrefix);
+ HostAndPortRange expected = new HostAndPortRange("127.0.0.1", 8080, 8090);
+ assertEquals(expected, actual);
+ }
+
+ /**
+ * Tests correct input address with IPv4 host and single port.
+ *
+ * @throws IgniteCheckedException on incorrect host/port
+ */
+ @Test
+ public void testParseIPv4WithSinglePort() throws IgniteCheckedException {
+ String addrStr = "127.0.0.1:8080";
+ String errMsgPrefix = "";
+ int dfltPortFrom = 18360;
+ int dfltPortTo = 18362;
+ HostAndPortRange actual = HostAndPortRange.parse(addrStr, dfltPortFrom, dfltPortTo, errMsgPrefix);
+ HostAndPortRange expected = new HostAndPortRange("127.0.0.1", 8080, 8080);
+ assertEquals(expected, actual);
+ }
+
+ /**
+ * Tests correct input address with IPv4 host and no port.
+ *
+ * @throws IgniteCheckedException on incorrect host/port
+ */
+ @Test
+ public void testParseIPv4NoPort() throws IgniteCheckedException {
+ String addrStr = "127.0.0.1";
+ String errMsgPrefix = "";
+ int dfltPortFrom = 18360;
+ int dfltPortTo = 18362;
+ HostAndPortRange actual = HostAndPortRange.parse(addrStr, dfltPortFrom, dfltPortTo, errMsgPrefix);
+ HostAndPortRange expected = new HostAndPortRange("127.0.0.1", 18360, 18362);
+ assertEquals(expected, actual);
+ }
+
+ /**
+ * Tests correct input address with IPv6 host and port range.
+ *
+ * @throws IgniteCheckedException on incorrect host/port
+ */
+ @Test
+ public void testParseIPv6WithPortRange() throws IgniteCheckedException {
+ String addrStr = "[::1]:8080..8090";
+ String errMsgPrefix = "";
+ int dfltPortFrom = 18360;
+ int dfltPortTo = 18362;
+ HostAndPortRange actual = HostAndPortRange.parse(addrStr, dfltPortFrom, dfltPortTo, errMsgPrefix);
+ HostAndPortRange expected = new HostAndPortRange("::1", 8080, 8090);
+ assertEquals(expected, actual);
+ }
+
+ /**
+ * Tests correct input address with IPv6 host and single port.
+ *
+ * @throws IgniteCheckedException on incorrect host/port
+ */
+ @Test
+ public void testParseIPv6WithSinglePort() throws IgniteCheckedException {
+ String addrStr = "[3ffe:2a00:100:7031::]:8080";
+ String errMsgPrefix = "";
+ int dfltPortFrom = 18360;
+ int dfltPortTo = 18362;
+ HostAndPortRange actual = HostAndPortRange.parse(addrStr, dfltPortFrom, dfltPortTo, errMsgPrefix);
+ HostAndPortRange expected = new HostAndPortRange("3ffe:2a00:100:7031::", 8080, 8080);
+ assertEquals(expected, actual);
+ }
+
+ /**
+ * Tests correct input address with IPv6 host and no port.
+ *
+ * @throws IgniteCheckedException on incorrect host/port
+ */
+ @Test
+ public void testParseIPv6NoPort() throws IgniteCheckedException {
+ String addrStr = "::FFFF:129.144.52.38";
+ String errMsgPrefix = "";
+ int dfltPortFrom = 18360;
+ int dfltPortTo = 18362;
+ HostAndPortRange actual = HostAndPortRange.parse(addrStr, dfltPortFrom, dfltPortTo, errMsgPrefix);
+ HostAndPortRange expected = new HostAndPortRange("::FFFF:129.144.52.38", 18360, 18362);
+ assertEquals(expected, actual);
+ }
+
+ @Rule
+ public ExpectedException expectedEx = ExpectedException.none();
+
+ /**
+ * Tests incorrect input address with IPv6 host (no brackets) and port.
+ *
+ * @throws IgniteCheckedException on incorrect host/port
+ */
+ @Test
+ public void testParseIPv6IncorrectHost() throws IgniteCheckedException {
+ expectedEx.expect(IgniteCheckedException.class);
+ expectedEx.expectMessage("IPv6 is incorrect");
+ expectedEx.expectCause(IsInstanceOf.instanceOf(UnknownHostException.class));
+ String addrStr = "3ffe:2a00:100:7031";
+ String errMsgPrefix = "";
+ int dfltPortFrom = 18360;
+ int dfltPortTo = 18362;
+ HostAndPortRange.parse(addrStr, dfltPortFrom, dfltPortTo, errMsgPrefix);
+ }
+
+ /**
+ * Tests empty host and port.
+ *
+ * @throws IgniteCheckedException on incorrect host/port
+ */
+ @Test
+ public void testParseNoHost() throws IgniteCheckedException {
+ expectedEx.expect(IgniteCheckedException.class);
+ expectedEx.expectMessage("Host name is empty");
+ String addrStr = ":8080";
+ String errMsgPrefix = "";
+ int dfltPortFrom = 18360;
+ int dfltPortTo = 18362;
+ HostAndPortRange.parse(addrStr, dfltPortFrom, dfltPortTo, errMsgPrefix);
+ }
+
+ /**
+ * Tests empty address string.
+ *
+ * @throws IgniteCheckedException on incorrect host/port
+ */
+ @Test
+ public void testParseNoAddress() throws IgniteCheckedException {
+ expectedEx.expect(IgniteCheckedException.class);
+ expectedEx.expectMessage("Address is empty");
+ String addrStr = "";
+ String errMsgPrefix = "";
+ int dfltPortFrom = 18360;
+ int dfltPortTo = 18362;
+ HostAndPortRange actual = HostAndPortRange.parse(addrStr, dfltPortFrom, dfltPortTo, errMsgPrefix);
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteUtilSelfTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteUtilSelfTestSuite.java
index 6e1dd3a..99b380d 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteUtilSelfTestSuite.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteUtilSelfTestSuite.java
@@ -24,6 +24,7 @@ import org.apache.ignite.internal.util.DistributedProcessCoordinatorLeftTest;
import org.apache.ignite.internal.util.GridArraysSelfTest;
import org.apache.ignite.internal.util.GridConcurrentMultiPairQueueTest;
import org.apache.ignite.internal.util.GridCountDownCallbackTest;
+import org.apache.ignite.internal.util.HostAndPortRangeTest;
import org.apache.ignite.internal.util.IgniteDevOnlyLogTest;
import org.apache.ignite.internal.util.IgniteExceptionRegistrySelfTest;
import org.apache.ignite.internal.util.IgniteUtilsSelfTest;
@@ -138,7 +139,9 @@ import org.junit.runners.Suite;
DistributedProcessCoordinatorLeftTest.class,
- BasicRateLimiterTest.class
+ BasicRateLimiterTest.class,
+
+ HostAndPortRangeTest.class
})
public class IgniteUtilSelfTestSuite {
}