diff --git a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HeaderUtils.java b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HeaderUtils.java
index f35419fb64..bc4de628d8 100644
--- a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HeaderUtils.java
+++ b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HeaderUtils.java
@@ -46,12 +46,12 @@
import static io.servicetalk.http.api.HttpHeaderNames.TRANSFER_ENCODING;
import static io.servicetalk.http.api.HttpHeaderNames.VARY;
import static io.servicetalk.http.api.HttpHeaderValues.CHUNKED;
-import static io.servicetalk.http.api.NetUtils.isValidIpV4Address;
-import static io.servicetalk.http.api.NetUtils.isValidIpV6Address;
import static io.servicetalk.http.api.UriUtils.TCHAR_HMASK;
import static io.servicetalk.http.api.UriUtils.TCHAR_LMASK;
import static io.servicetalk.http.api.UriUtils.isBitSet;
import static io.servicetalk.utils.internal.CharsetUtils.standardCharsets;
+import static io.servicetalk.utils.internal.NetworkUtils.isValidIpV4Address;
+import static io.servicetalk.utils.internal.NetworkUtils.isValidIpV6Address;
import static java.lang.Math.min;
import static java.lang.System.lineSeparator;
import static java.nio.charset.StandardCharsets.UTF_8;
diff --git a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/SingleAddressHttpClientBuilder.java b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/SingleAddressHttpClientBuilder.java
index 368d3bf111..b7cbcd3e85 100644
--- a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/SingleAddressHttpClientBuilder.java
+++ b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/SingleAddressHttpClientBuilder.java
@@ -94,7 +94,7 @@ SingleAddressHttpClientBuilder enableWireLogging(String loggerName,
SingleAddressHttpClientBuilder protocols(HttpProtocolConfig... protocols);
/**
- * Configures automatically setting {@code Host} headers by inferring from the address or {@link HttpMetaData}.
+ * Configures automatically setting {@code Host} headers by inferring from the address.
*
* When {@code false} is passed, this setting disables the default filter such that no {@code Host} header will be
* manipulated.
diff --git a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/DefaultSingleAddressHttpClientBuilder.java b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/DefaultSingleAddressHttpClientBuilder.java
index ea1f4e2aca..9c2c394e10 100644
--- a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/DefaultSingleAddressHttpClientBuilder.java
+++ b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/DefaultSingleAddressHttpClientBuilder.java
@@ -45,6 +45,7 @@
import io.servicetalk.http.api.StreamingHttpConnectionFilterFactory;
import io.servicetalk.http.api.StreamingHttpRequest;
import io.servicetalk.http.api.StreamingHttpRequestResponseFactory;
+import io.servicetalk.http.utils.HostHeaderHttpRequesterFilter;
import io.servicetalk.logging.api.LogLevel;
import io.servicetalk.transport.api.ClientSslConfig;
import io.servicetalk.transport.api.ExecutionStrategy;
@@ -448,13 +449,22 @@ public DefaultSingleAddressHttpClientBuilder appendConnectionFilter(
requireNonNull(factory);
connectionFilterFactory = appendConnectionFilter(connectionFilterFactory, factory);
strategyComputation.add(factory);
+ ifHostHeaderHttpRequesterFilter(factory);
return this;
}
@Override
public DefaultSingleAddressHttpClientBuilder appendConnectionFilter(
final Predicate predicate, final StreamingHttpConnectionFilterFactory factory) {
- return appendConnectionFilter(toConditionalConnectionFilterFactory(predicate, factory));
+ appendConnectionFilter(toConditionalConnectionFilterFactory(predicate, factory));
+ ifHostHeaderHttpRequesterFilter(factory);
+ return this;
+ }
+
+ private void ifHostHeaderHttpRequesterFilter(final Object filter) {
+ if (filter instanceof HostHeaderHttpRequesterFilter) {
+ addHostHeaderFallbackFilter = false;
+ }
}
// Use another method to keep final references and avoid StackOverflowError
@@ -491,7 +501,9 @@ public DefaultSingleAddressHttpClientBuilder appendClientFilter(
ensureSingleRetryFilter();
retryingHttpRequesterFilter = (RetryingHttpRequesterFilter) factory;
}
- return appendClientFilter(toConditionalClientFilterFactory(predicate, factory));
+ appendClientFilter(toConditionalClientFilterFactory(predicate, factory));
+ ifHostHeaderHttpRequesterFilter(factory);
+ return this;
}
private void ensureSingleRetryFilter() {
@@ -518,6 +530,7 @@ public DefaultSingleAddressHttpClientBuilder appendClientFilter(
}
clientFilterFactory = appendFilter(clientFilterFactory, factory);
strategyComputation.add(factory);
+ ifHostHeaderHttpRequesterFilter(factory);
return this;
}
diff --git a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClientConfig.java b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClientConfig.java
index 842ece52fb..a6efeec79e 100644
--- a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClientConfig.java
+++ b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClientConfig.java
@@ -22,9 +22,9 @@
import java.util.List;
import javax.annotation.Nullable;
-import static io.netty.util.NetUtil.isValidIpV4Address;
-import static io.netty.util.NetUtil.isValidIpV6Address;
import static io.servicetalk.http.netty.HttpServerConfig.httpAlpnProtocols;
+import static io.servicetalk.utils.internal.NetworkUtils.isValidIpV4Address;
+import static io.servicetalk.utils.internal.NetworkUtils.isValidIpV6Address;
final class HttpClientConfig {
diff --git a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HostHeaderHttpRequesterFilterTest.java b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HostHeaderHttpRequesterFilterTest.java
index 8bec182469..8a731e214f 100644
--- a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HostHeaderHttpRequesterFilterTest.java
+++ b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HostHeaderHttpRequesterFilterTest.java
@@ -22,6 +22,7 @@
import io.servicetalk.http.api.HttpRequest;
import io.servicetalk.http.api.HttpResponse;
import io.servicetalk.http.api.ReservedBlockingHttpConnection;
+import io.servicetalk.http.utils.HostHeaderHttpRequesterFilter;
import io.servicetalk.transport.api.ServerContext;
import org.junit.jupiter.params.ParameterizedTest;
@@ -128,7 +129,6 @@ void clientBuilderAppendClientFilter(HttpVersionConfig httpVersionConfig) throws
try (ServerContext context = buildServer();
BlockingHttpClient client = forSingleAddress(serverHostAndPort(context))
.protocols(httpVersionConfig.config())
- .hostHeaderFallback(false) // turn off the default
.appendClientFilter(new HostHeaderHttpRequesterFilter("foo.bar:-1"))
.buildBlocking()) {
assertResponse(client, null, "foo.bar:-1");
@@ -142,7 +142,6 @@ void clientBuilderAppendConnectionFilter(HttpVersionConfig httpVersionConfig) th
try (ServerContext context = buildServer();
BlockingHttpClient client = forSingleAddress(serverHostAndPort(context))
.protocols(httpVersionConfig.config())
- .hostHeaderFallback(false) // turn off the default
.appendConnectionFilter(new HostHeaderHttpRequesterFilter("foo.bar:-1"))
.buildBlocking()) {
assertResponse(client, null, "foo.bar:-1");
@@ -156,7 +155,6 @@ void reserveConnection(HttpVersionConfig httpVersionConfig) throws Exception {
try (ServerContext context = buildServer();
BlockingHttpClient client = HttpClients.forResolvedAddress(serverHostAndPort(context))
.protocols(httpVersionConfig.config())
- .hostHeaderFallback(false) // turn off the default
.appendConnectionFilter(new HostHeaderHttpRequesterFilter("foo.bar:-1"))
.buildBlocking();
ReservedBlockingHttpConnection conn = client.reserveConnection(client.get("/"))) {
@@ -172,7 +170,6 @@ void clientBuilderAppendClientFilterExplicitHostHeader(HttpVersionConfig httpVer
try (ServerContext context = buildServer();
BlockingHttpClient client = forSingleAddress(serverHostAndPort(context))
.protocols(httpVersionConfig.config())
- .hostHeaderFallback(false) // turn off the default
.appendClientFilter(new HostHeaderHttpRequesterFilter("foo.bar:-1"))
.buildBlocking()) {
assertResponse(client, "bar.only:-1", "bar.only:-1");
diff --git a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/RedirectingClientAndConnectionFilterTest.java b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/RedirectingClientAndConnectionFilterTest.java
index 0b897a3d4e..3e355ee60b 100644
--- a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/RedirectingClientAndConnectionFilterTest.java
+++ b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/RedirectingClientAndConnectionFilterTest.java
@@ -22,6 +22,7 @@
import io.servicetalk.http.api.HttpRequest;
import io.servicetalk.http.api.HttpResponse;
import io.servicetalk.http.netty.ConditionalFilterFactory.FilterFactory;
+import io.servicetalk.http.utils.HostHeaderHttpRequesterFilter;
import io.servicetalk.http.utils.RedirectingHttpRequesterFilter;
import io.servicetalk.transport.api.HostAndPort;
diff --git a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HostHeaderHttpRequesterFilter.java b/servicetalk-http-utils/src/main/java/io/servicetalk/http/utils/HostHeaderHttpRequesterFilter.java
similarity index 84%
rename from servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HostHeaderHttpRequesterFilter.java
rename to servicetalk-http-utils/src/main/java/io/servicetalk/http/utils/HostHeaderHttpRequesterFilter.java
index ccb3596d5b..43dc3bf0e5 100644
--- a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HostHeaderHttpRequesterFilter.java
+++ b/servicetalk-http-utils/src/main/java/io/servicetalk/http/utils/HostHeaderHttpRequesterFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2018-2021 Apple Inc. and the ServiceTalk project authors
+ * Copyright © 2018-2022 Apple Inc. and the ServiceTalk project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package io.servicetalk.http.netty;
+package io.servicetalk.http.utils;
import io.servicetalk.concurrent.api.Single;
import io.servicetalk.http.api.FilterableStreamingHttpClient;
@@ -21,6 +21,7 @@
import io.servicetalk.http.api.HttpExecutionStrategies;
import io.servicetalk.http.api.HttpExecutionStrategy;
import io.servicetalk.http.api.HttpHeaderNames;
+import io.servicetalk.http.api.HttpRequestMetaData;
import io.servicetalk.http.api.StreamingHttpClientFilter;
import io.servicetalk.http.api.StreamingHttpClientFilterFactory;
import io.servicetalk.http.api.StreamingHttpConnectionFilter;
@@ -29,24 +30,26 @@
import io.servicetalk.http.api.StreamingHttpRequester;
import io.servicetalk.http.api.StreamingHttpResponse;
-import static io.netty.util.NetUtil.isValidIpV6Address;
import static io.servicetalk.buffer.api.CharSequences.newAsciiString;
import static io.servicetalk.concurrent.api.Single.defer;
import static io.servicetalk.http.api.HttpHeaderNames.HOST;
import static io.servicetalk.http.api.HttpProtocolVersion.HTTP_1_0;
+import static io.servicetalk.utils.internal.NetworkUtils.isValidIpV6Address;
/**
- * A filter which will apply a fallback value for the {@link HttpHeaderNames#HOST} header if one is not present.
+ * A filter which will set a {@link HttpHeaderNames#HOST} header with the fallback value if the header is not already
+ * present in {@link HttpRequestMetaData}.
*/
-final class HostHeaderHttpRequesterFilter implements StreamingHttpClientFilterFactory,
- StreamingHttpConnectionFilterFactory {
+public final class HostHeaderHttpRequesterFilter implements StreamingHttpClientFilterFactory,
+ StreamingHttpConnectionFilterFactory {
private final CharSequence fallbackHost;
/**
* Create a new instance.
+ *
* @param fallbackHost The address to use as a fallback if a {@link HttpHeaderNames#HOST} header is not present.
*/
- HostHeaderHttpRequesterFilter(CharSequence fallbackHost) {
+ public HostHeaderHttpRequesterFilter(CharSequence fallbackHost) {
this.fallbackHost = newAsciiString(isValidIpV6Address(fallbackHost) && fallbackHost.charAt(0) != '[' ?
"[" + fallbackHost + "]" : fallbackHost.toString());
}
diff --git a/servicetalk-transport-netty-internal/src/main/java/io/servicetalk/transport/netty/internal/BuilderUtils.java b/servicetalk-transport-netty-internal/src/main/java/io/servicetalk/transport/netty/internal/BuilderUtils.java
index dacfeac5a4..a51e6d1c10 100644
--- a/servicetalk-transport-netty-internal/src/main/java/io/servicetalk/transport/netty/internal/BuilderUtils.java
+++ b/servicetalk-transport-netty-internal/src/main/java/io/servicetalk/transport/netty/internal/BuilderUtils.java
@@ -39,7 +39,6 @@
import io.netty.incubator.channel.uring.IOUringDatagramChannel;
import io.netty.incubator.channel.uring.IOUringServerSocketChannel;
import io.netty.incubator.channel.uring.IOUringSocketChannel;
-import io.netty.util.NetUtil;
import java.io.Closeable;
import java.io.IOException;
@@ -51,6 +50,7 @@
import javax.annotation.Nullable;
import static io.netty.util.NetUtil.createByteArrayFromIpAddressString;
+import static io.netty.util.NetUtil.toAddressString;
import static io.servicetalk.transport.netty.internal.NativeTransportUtils.useEpoll;
import static io.servicetalk.transport.netty.internal.NativeTransportUtils.useIoUring;
import static io.servicetalk.transport.netty.internal.NativeTransportUtils.useKQueue;
@@ -203,9 +203,9 @@ public static String formatCanonicalAddress(SocketAddress address) {
if (inetAddress == null) {
return address.toString();
} else if (inetAddress instanceof Inet6Address) {
- return '[' + NetUtil.toAddressString(inetAddress) + "]:" + inetSocketAddress.getPort();
+ return '[' + toAddressString(inetAddress) + "]:" + inetSocketAddress.getPort();
} else {
- return NetUtil.toAddressString(inetAddress) + ':' + inetSocketAddress.getPort();
+ return toAddressString(inetAddress) + ':' + inetSocketAddress.getPort();
}
}
return address.toString();
diff --git a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/NetUtils.java b/servicetalk-utils-internal/src/main/java/io/servicetalk/utils/internal/NetworkUtils.java
similarity index 91%
rename from servicetalk-http-api/src/main/java/io/servicetalk/http/api/NetUtils.java
rename to servicetalk-utils-internal/src/main/java/io/servicetalk/utils/internal/NetworkUtils.java
index 551588a512..8b801126df 100644
--- a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/NetUtils.java
+++ b/servicetalk-utils-internal/src/main/java/io/servicetalk/utils/internal/NetworkUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2018, 2021 Apple Inc. and the ServiceTalk project authors
+ * Copyright © 2018, 2021-2022 Apple Inc. and the ServiceTalk project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,29 +28,37 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
-package io.servicetalk.http.api;
+package io.servicetalk.utils.internal;
import static io.servicetalk.buffer.api.CharSequences.indexOf;
-final class NetUtils {
+/**
+ * Network-related utilities.
+ *
+ * This class borrowed some of its methods from
+ * NetUtil class
+ * which was part of Netty.
+ */
+public final class NetworkUtils {
- private NetUtils() {
+ private NetworkUtils() {
// no instances
}
/**
* Takes a string and parses it to see if it is a valid IPV4 address.
*
+ * @param ip the IP-address to validate
* @return true, if the string represents an IPV4 address in dotted notation, false otherwise.
*/
- static boolean isValidIpV4Address(final CharSequence ip) {
+ public static boolean isValidIpV4Address(final CharSequence ip) {
return isValidIpV4Address(ip, 0, ip.length());
}
private static boolean isValidIpV4Address(final CharSequence ip, int from, int toExclusive) {
int len = toExclusive - from;
int i;
- return len <= 15 && len > 7 &&
+ return len <= 15 && len >= 7 &&
(i = indexOf(ip, '.', from + 1)) > 0 && isValidIpV4Word(ip, from, i) &&
(i = indexOf(ip, '.', from = i + 2)) > 0 && isValidIpV4Word(ip, from - 1, i) &&
(i = indexOf(ip, '.', from = i + 2)) > 0 && isValidIpV4Word(ip, from - 1, i) &&
@@ -60,9 +68,10 @@ private static boolean isValidIpV4Address(final CharSequence ip, int from, int t
/**
* Takes a string and parses it to see if it is a valid IPV6 address.
*
+ * @param ip the IP-address to validate
* @return true, if the string represents an IPV6 address
*/
- static boolean isValidIpV6Address(final CharSequence ip) {
+ public static boolean isValidIpV6Address(final CharSequence ip) {
int end = ip.length();
if (end < 2) {
return false;
diff --git a/servicetalk-utils-internal/src/test/java/io/servicetalk/utils/internal/NetworkUtilsTest.java b/servicetalk-utils-internal/src/test/java/io/servicetalk/utils/internal/NetworkUtilsTest.java
new file mode 100644
index 0000000000..0ad1442e91
--- /dev/null
+++ b/servicetalk-utils-internal/src/test/java/io/servicetalk/utils/internal/NetworkUtilsTest.java
@@ -0,0 +1,447 @@
+/*
+ * Copyright © 2022 Apple Inc. and the ServiceTalk project authors
+ *
+ * Licensed 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.
+ */
+/*
+ * Copyright 2012 The Netty Project
+ *
+ * The Netty Project 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:
+ *
+ * https://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 io.servicetalk.utils.internal;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static io.servicetalk.utils.internal.NetworkUtils.isValidIpV4Address;
+import static io.servicetalk.utils.internal.NetworkUtils.isValidIpV6Address;
+import static java.util.Arrays.asList;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@SuppressWarnings("PMD.AvoidUsingHardCodedIP")
+class NetworkUtilsTest {
+
+ private static final List validIpV4Hosts = asList(
+ "192.168.1.0",
+ "10.255.255.254",
+ "172.18.5.4",
+ "0.0.0.0",
+ "127.0.0.1",
+ "255.255.255.255",
+ "1.2.3.4");
+
+ private static final List invalidIpV4Hosts = asList(
+ "Garbage",
+ "1.256.3.4",
+ "256.0.0.1",
+ "1.1.1.1.1",
+ "x.255.255.255",
+ "0.1:0.0",
+ "0.1.0.0:",
+ "127.0.0.",
+ "1.2..4",
+ "192.0.1",
+ "192.0.1.1.1",
+ "192.0.1.a",
+ "19a.0.1.1",
+ "a.0.1.1",
+ ".0.1.1",
+ "127.0.0",
+ "192.0.1.256",
+ "0.0.200.259",
+ "1.1.-1.1",
+ "1.1. 1.1",
+ "1.1.1.1 ",
+ "1.1.+1.1",
+ "0.0x1.0.255",
+ "0.01x.0.255",
+ "0.x01.0.255",
+ "0.-.0.0",
+ "0..0.0",
+ "0.A.0.0",
+ "0.1111.0.0",
+ "...");
+
+ private static final List validIpV6Hosts = asList(
+ "::ffff:5.6.7.8",
+ "fdf8:f53b:82e4::53",
+ "fe80::200:5aee:feaa:20a2",
+ "2001::1",
+ "2001:0000:4136:e378:8000:63bf:3fff:fdd2",
+ "2001:0002:6c::430",
+ "2001:10:240:ab::a",
+ "2002:cb0a:3cdd:1::1",
+ "2001:db8:8:4::2",
+ "ff01:0:0:0:0:0:0:2",
+ "[fdf8:f53b:82e4::53]",
+ "[fe80::200:5aee:feaa:20a2]",
+ "[2001::1]",
+ "[2001:0000:4136:e378:8000:63bf:3fff:fdd2]",
+ "0:1:2:3:4:5:6:789a",
+ "0:1:2:3::f",
+ "0:0:0:0:0:0:10.0.0.1",
+ "0:0:0:0:0::10.0.0.1",
+ "0:0:0:0::10.0.0.1",
+ "::0:0:0:0:0:10.0.0.1",
+ "0::0:0:0:0:10.0.0.1",
+ "0:0::0:0:0:10.0.0.1",
+ "0:0:0::0:0:10.0.0.1",
+ "0:0:0:0::0:10.0.0.1",
+ "0:0:0:0:0:ffff:10.0.0.1",
+ "::ffff:192.168.0.1",
+ // Test if various interface names after the percent sign are recognized.
+ "[::1%1]",
+ "[::1%eth0]",
+ "[::1%%]",
+ "0:0:0:0:0:ffff:10.0.0.1%",
+ "0:0:0:0:0:ffff:10.0.0.1%1",
+ "[0:0:0:0:0:ffff:10.0.0.1%1]",
+ "[0:0:0:0:0::10.0.0.1%1]",
+ "[::0:0:0:0:ffff:10.0.0.1%1]",
+ "::0:0:0:0:ffff:10.0.0.1%1",
+ "::1%1",
+ "::1%eth0",
+ "::1%%",
+ // Tests with leading or trailing compression
+ "0:0:0:0:0:0:0::",
+ "0:0:0:0:0:0::",
+ "0:0:0:0:0::",
+ "0:0:0:0::",
+ "0:0:0::",
+ "0:0::",
+ "0::",
+ "::",
+ "::0",
+ "::0:0",
+ "::0:0:0",
+ "::0:0:0:0",
+ "::0:0:0:0:0",
+ "::0:0:0:0:0:0",
+ "::0:0:0:0:0:0:0");
+
+ private static final List invalidIpV6Hosts = asList(
+ // Test method with garbage.
+ "Obvious Garbage",
+ // Test method with preferred style, too many :
+ "0:1:2:3:4:5:6:7:8",
+ // Test method with preferred style, not enough :
+ "0:1:2:3:4:5:6",
+ // Test method with preferred style, bad digits.
+ "0:1:2:3:4:5:6:x",
+ // Test method with preferred style, adjacent :
+ "0:1:2:3:4:5:6::7",
+ // Too many : separators trailing
+ "0:1:2:3:4:5:6:7::",
+ // Too many : separators leading
+ "::0:1:2:3:4:5:6:7",
+ // Too many : separators trailing
+ "1:2:3:4:5:6:7:",
+ // Too many : separators leading
+ ":1:2:3:4:5:6:7",
+ // Compression with : separators trailing
+ "0:1:2:3:4:5::7:",
+ "0:1:2:3:4::7:",
+ "0:1:2:3::7:",
+ "0:1:2::7:",
+ "0:1::7:",
+ "0::7:",
+ // Compression at start with : separators trailing
+ "::0:1:2:3:4:5:7:",
+ "::0:1:2:3:4:7:",
+ "::0:1:2:3:7:",
+ "::0:1:2:7:",
+ "::0:1:7:",
+ "::7:",
+ // The : separators leading and trailing
+ ":1:2:3:4:5:6:7:",
+ ":1:2:3:4:5:6:",
+ ":1:2:3:4:5:",
+ ":1:2:3:4:",
+ ":1:2:3:",
+ ":1:2:",
+ ":1:",
+ // Compression with : separators leading
+ ":1::2:3:4:5:6:7",
+ ":1::3:4:5:6:7",
+ ":1::4:5:6:7",
+ ":1::5:6:7",
+ ":1::6:7",
+ ":1::7",
+ ":1:2:3:4:5:6::7",
+ ":1:3:4:5:6::7",
+ ":1:4:5:6::7",
+ ":1:5:6::7",
+ ":1:6::7",
+ ":1::",
+ // Compression trailing with : separators leading
+ ":1:2:3:4:5:6:7::",
+ ":1:3:4:5:6:7::",
+ ":1:4:5:6:7::",
+ ":1:5:6:7::",
+ ":1:6:7::",
+ ":1:7::",
+ // Double compression
+ "1::2:3:4:5:6::",
+ "::1:2:3:4:5::6",
+ "::1:2:3:4:5:6::",
+ "::1:2:3:4:5::",
+ "::1:2:3:4::",
+ "::1:2:3::",
+ "::1:2::",
+ "::0::",
+ "12::0::12",
+ // Too many : separators leading 0
+ "0::1:2:3:4:5:6:7",
+ // Test method with preferred style, too many digits.
+ "0:1:2:3:4:5:6:789abcdef",
+ // Test method with compressed style, bad digits.
+ "0:1:2:3::x",
+ // Test method with compressed style, too many adjacent :
+ "0:1:2:::3",
+ // Test method with compressed style, too many digits.
+ "0:1:2:3::abcde",
+ // Test method with compressed style, not enough :
+ "0:1",
+ // Test method with ipv4 style, bad ipv6 digits.
+ "0:0:0:0:0:x:10.0.0.1",
+ // Test method with ipv4 style, bad ipv4 digits.
+ "0:0:0:0:0:0:10.0.0.x",
+ // Test method with ipv4 style, too many ipv6 digits.
+ "0:0:0:0:0:00000:10.0.0.1",
+ // Test method with ipv4 style, too many :
+ "0:0:0:0:0:0:0:10.0.0.1",
+ // Test method with ipv4 style, not enough :
+ "0:0:0:0:0:10.0.0.1",
+ // Test method with ipv4 style, too many .
+ "0:0:0:0:0:0:10.0.0.0.1",
+ // Test method with ipv4 style, not enough .
+ "0:0:0:0:0:0:10.0.1",
+ // Test method with ipv4 style, adjacent .
+ "0:0:0:0:0:0:10..0.0.1",
+ // Test method with ipv4 style, leading .
+ "0:0:0:0:0:0:.0.0.1",
+ // Test method with ipv4 style, leading .
+ "0:0:0:0:0:0:.10.0.0.1",
+ // Test method with ipv4 style, trailing .
+ "0:0:0:0:0:0:10.0.0.",
+ // Test method with ipv4 style, trailing .
+ "0:0:0:0:0:0:10.0.0.1.",
+ // Test method with compressed ipv4 style, bad ipv6 digits.
+ "::fffx:192.168.0.1",
+ // Test method with compressed ipv4 style, bad ipv4 digits.
+ "::ffff:192.168.0.x",
+ // Test method with compressed ipv4 style, too many adjacent :
+ ":::ffff:192.168.0.1",
+ // Test method with compressed ipv4 style, too many ipv6 digits.
+ "::fffff:192.168.0.1",
+ // Test method with compressed ipv4 style, too many ipv4 digits.
+ "::ffff:1923.168.0.1",
+ // Test method with compressed ipv4 style, not enough :
+ ":ffff:192.168.0.1",
+ // Test method with compressed ipv4 style, too many .
+ "::ffff:192.168.0.1.2",
+ // Test method with compressed ipv4 style, not enough .
+ "::ffff:192.168.0",
+ // Test method with compressed ipv4 style, adjacent .
+ "::ffff:192.168..0.1",
+ // Test method, bad ipv6 digits.
+ "x:0:0:0:0:0:10.0.0.1",
+ // Test method, bad ipv4 digits.
+ "0:0:0:0:0:0:x.0.0.1",
+ // Test method, too many ipv6 digits.
+ "00000:0:0:0:0:0:10.0.0.1",
+ // Test method, too many ipv4 digits.
+ "0:0:0:0:0:0:10.0.0.1000",
+ // Test method, too many :
+ "0:0:0:0:0:0:0:10.0.0.1",
+ // Test method, not enough :
+ "0:0:0:0:0:10.0.0.1",
+ // Test method, out of order trailing :
+ "0:0:0:0:0:10.0.0.1:",
+ // Test method, out of order leading :
+ ":0:0:0:0:0:10.0.0.1",
+ // Test method, out of order leading :
+ "0:0:0:0::10.0.0.1:",
+ // Test method, out of order trailing :
+ ":0:0:0:0::10.0.0.1",
+ // Test method, too many .
+ "0:0:0:0:0:0:10.0.0.0.1",
+ // Test method, not enough .
+ "0:0:0:0:0:0:10.0.1",
+ // Test method, adjacent .
+ "0:0:0:0:0:0:10.0.0..1",
+ // Empty contents
+ "",
+ // Invalid single compression
+ ":",
+ ":::",
+ // Trailing : (max number of : = 8)
+ "2001:0:4136:e378:8000:63bf:3fff:fdd2:",
+ // Leading : (max number of : = 8)
+ ":aaaa:bbbb:cccc:dddd:eeee:ffff:1111:2222",
+ // Invalid character
+ "1234:2345:3456:4567:5678:6789::X890",
+ // Trailing . in IPv4
+ "::ffff:255.255.255.255.",
+ // To many characters in IPv4
+ "::ffff:0.0.1111.0",
+ // Test method, adjacent .
+ "::ffff:0.0..0",
+ // Not enough IPv4 entries trailing .
+ "::ffff:127.0.0.",
+ // Invalid trailing IPv4 character
+ "::ffff:127.0.0.a",
+ // Invalid leading IPv4 character
+ "::ffff:a.0.0.1",
+ // Invalid middle IPv4 character
+ "::ffff:127.a.0.1",
+ // Invalid middle IPv4 character
+ "::ffff:127.0.a.1",
+ // Not enough IPv4 entries no trailing .
+ "::ffff:1.2.4",
+ // Extra IPv4 entry
+ "::ffff:192.168.0.1.255",
+ // Not enough IPv6 content
+ ":ffff:192.168.0.1.255",
+ // Intermixed IPv4 and IPv6 symbols
+ "::ffff:255.255:255.255.",
+ // Invalid IPv4 mapped address - invalid ipv4 separator
+ "0:0:0::0:0:00f.0.0.1",
+ // Invalid IPv4 mapped address - not enough f's
+ "0:0:0:0:0:fff:1.0.0.1",
+ // Invalid IPv4 mapped address - not IPv4 mapped, not IPv4 compatible
+ "0:0:0:0:0:ff00:1.0.0.1",
+ // Invalid IPv4 mapped address - not IPv4 mapped, not IPv4 compatible
+ "0:0:0:0:0:ff:1.0.0.1",
+ // Invalid IPv4 mapped address - too many f's
+ "0:0:0:0:0:fffff:1.0.0.1",
+ // Invalid IPv4 mapped address - too many bytes (too many 0's)
+ "0:0:0:0:0:0:ffff:1.0.0.1",
+ // Invalid IPv4 mapped address - too many bytes (too many 0's)
+ "::0:0:0:0:0:ffff:1.0.0.1",
+ // Invalid IPv4 mapped address - too many bytes (too many 0's)
+ "0:0:0:0:0:0::1.0.0.1",
+ // Invalid IPv4 mapped address - too many bytes (too many 0's)
+ "0:0:0:0:0:00000:1.0.0.1",
+ // Invalid IPv4 mapped address - too few bytes (not enough 0's)
+ "0:0:0:0:ffff:1.0.0.1",
+ // Invalid IPv4 mapped address - too few bytes (not enough 0's)
+ "ffff:192.168.0.1",
+ // Invalid IPv4 mapped address - 0's after the mapped ffff indicator
+ "0:0:0:0:0:ffff::10.0.0.1",
+ // Invalid IPv4 mapped address - 0's after the mapped ffff indicator
+ "0:0:0:0:ffff::10.0.0.1",
+ // Invalid IPv4 mapped address - 0's after the mapped ffff indicator
+ "0:0:0:ffff::10.0.0.1",
+ // Invalid IPv4 mapped address - 0's after the mapped ffff indicator
+ "0:0:ffff::10.0.0.1",
+ // Invalid IPv4 mapped address - 0's after the mapped ffff indicator
+ "0:ffff::10.0.0.1",
+ // Invalid IPv4 mapped address - 0's after the mapped ffff indicator
+ "ffff::10.0.0.1",
+ // Invalid IPv4 mapped address - not all 0's before the mapped separator
+ "1:0:0:0:0:ffff:10.0.0.1",
+ // Address that is similar to IPv4 mapped, but is invalid
+ "0:0:0:0:ffff:ffff:1.0.0.1",
+ // Valid number of separators, but invalid IPv4 format
+ "::1:2:3:4:5:6.7.8.9",
+ // Too many digits
+ "0:0:0:0:0:0:ffff:10.0.0.1",
+ // Invalid IPv4 format
+ ":1.2.3.4",
+ // Invalid IPv4 format
+ "::.2.3.4",
+ // Invalid IPv4 format
+ "::ffff:0.1.2.");
+
+ @Test
+ void testIsValidIpV4Address() {
+ for (String host : validIpV4Hosts) {
+ assertTrue(isValidIpV4Address(host), host);
+ }
+ for (String host : invalidIpV4Hosts) {
+ assertFalse(isValidIpV4Address(host), host);
+ }
+ }
+
+ @Test
+ void testIsValidIpV6Address() {
+ for (String host : validIpV6Hosts) {
+ assertTrue(isValidIpV6Address(host), host);
+ if (host.charAt(0) != '[' && !host.contains("%")) {
+ String hostMod = '[' + host + ']';
+ assertTrue(isValidIpV6Address(hostMod), hostMod);
+
+ hostMod = host + '%';
+ assertTrue(isValidIpV6Address(hostMod), hostMod);
+
+ hostMod = host + "%eth1";
+ assertTrue(isValidIpV6Address(hostMod), hostMod);
+
+ hostMod = '[' + host + "%]";
+ assertTrue(isValidIpV6Address(hostMod), hostMod);
+
+ hostMod = '[' + host + "%1]";
+ assertTrue(isValidIpV6Address(hostMod), hostMod);
+
+ hostMod = '[' + host + "]%";
+ assertFalse(isValidIpV6Address(hostMod), hostMod);
+
+ hostMod = '[' + host + "]%1";
+ assertFalse(isValidIpV6Address(hostMod), hostMod);
+ }
+ }
+ for (String host : invalidIpV6Hosts) {
+ assertFalse(isValidIpV6Address(host), host);
+
+ String hostMod = '[' + host + ']';
+ assertFalse(isValidIpV6Address(hostMod), hostMod);
+
+ hostMod = host + '%';
+ assertFalse(isValidIpV6Address(hostMod), hostMod);
+
+ hostMod = host + "%eth1";
+ assertFalse(isValidIpV6Address(hostMod), hostMod);
+
+ hostMod = '[' + host + "%]";
+ assertFalse(isValidIpV6Address(hostMod), hostMod);
+
+ hostMod = '[' + host + "%1]";
+ assertFalse(isValidIpV6Address(hostMod), hostMod);
+
+ hostMod = '[' + host + "]%";
+ assertFalse(isValidIpV6Address(hostMod), hostMod);
+
+ hostMod = '[' + host + "]%1";
+ assertFalse(isValidIpV6Address(hostMod), hostMod);
+
+ hostMod = host + ']';
+ assertFalse(isValidIpV6Address(hostMod), hostMod);
+
+ hostMod = '[' + host;
+ assertFalse(isValidIpV6Address(hostMod), hostMod);
+ }
+ }
+}