From 79237708f7d2e48760cd2d76adeea99b3483a5b2 Mon Sep 17 00:00:00 2001 From: Santiago Pericasgeertsen Date: Mon, 22 Jan 2024 16:13:51 -0500 Subject: [PATCH] Cleans up and simplifies logic to determine which type of IP addresses (v4 or v6) to consider during name resolution. No longer inspect hosts interfaces to determine priority. Honors JVM properties defined for this purpose. Also updates TcpClientConnection to use whatever strategy specified by a user as part of a WebClient's configuration. --- webclient/api/pom.xml | 8 +++ .../api/DefaultAddressLookupFinder.java | 63 ++++--------------- .../webclient/api/TcpClientConnection.java | 21 +++++-- .../webclient/api/Ipv4LookupFinderTest.java | 33 ++++++++++ .../webclient/api/Ipv6LookupFinderTest.java | 36 +++++++++++ 5 files changed, 104 insertions(+), 57 deletions(-) create mode 100644 webclient/api/src/test/java/io/helidon/webclient/api/Ipv4LookupFinderTest.java create mode 100644 webclient/api/src/test/java/io/helidon/webclient/api/Ipv6LookupFinderTest.java diff --git a/webclient/api/pom.xml b/webclient/api/pom.xml index 819685b22aa..681874ca16b 100644 --- a/webclient/api/pom.xml +++ b/webclient/api/pom.xml @@ -161,6 +161,14 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + 1 + false + + diff --git a/webclient/api/src/main/java/io/helidon/webclient/api/DefaultAddressLookupFinder.java b/webclient/api/src/main/java/io/helidon/webclient/api/DefaultAddressLookupFinder.java index 3f69a7a873f..5f1f5942fe7 100644 --- a/webclient/api/src/main/java/io/helidon/webclient/api/DefaultAddressLookupFinder.java +++ b/webclient/api/src/main/java/io/helidon/webclient/api/DefaultAddressLookupFinder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023 Oracle and/or its affiliates. + * Copyright (c) 2022, 2024 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +16,10 @@ package io.helidon.webclient.api; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.util.Enumeration; - import io.helidon.common.LazyValue; +import static java.lang.System.Logger.Level; + /** * Heavily inspired by Netty. */ @@ -34,27 +30,22 @@ final class DefaultAddressLookupFinder { /** * {@code true} if IPv4 should be used even if the system supports both IPv4 and IPv6. */ - private static final LazyValue IPV4_PREFERRED = LazyValue.create(() -> { - return Boolean.getBoolean("java.net.preferIPv4Stack"); - }); + private static final LazyValue IPV4_PREFERRED = LazyValue.create(() -> + Boolean.getBoolean("java.net.preferIPv4Stack")); /** * {@code true} if an IPv6 address should be preferred when a host has both an IPv4 address and an IPv6 address. */ - private static final LazyValue IPV6_PREFERRED = LazyValue.create(() -> { - return Boolean.getBoolean("java.net.preferIPv6Addresses"); - }); + private static final LazyValue IPV6_PREFERRED = LazyValue.create(() -> + Boolean.getBoolean("java.net.preferIPv6Addresses")); private static final LazyValue DEFAULT_IP_VERSION = LazyValue.create(() -> { - if (IPV4_PREFERRED.get() || !anyInterfaceSupportsIpV6()) { - return DnsAddressLookup.IPV4; - } else { - if (IPV6_PREFERRED.get()) { - return DnsAddressLookup.IPV6_PREFERRED; - } else { - return DnsAddressLookup.IPV4_PREFERRED; - } + if (IPV4_PREFERRED.get() || !IPV6_PREFERRED.get()) { + LOGGER.log(Level.DEBUG, "Preferring IPv4 over IPv6 address resolution"); + return DnsAddressLookup.IPV4_PREFERRED; } + LOGGER.log(Level.DEBUG, "Preferring IPv6 over IPv4 address resolution"); + return DnsAddressLookup.IPV6_PREFERRED; }); private DefaultAddressLookupFinder() { @@ -64,34 +55,4 @@ private DefaultAddressLookupFinder() { static DnsAddressLookup defaultDnsAddressLookup() { return DEFAULT_IP_VERSION.get(); } - - /** - * Returns {@code true} if any {@link NetworkInterface} supports {@code IPv6}, {@code false} otherwise. - */ - private static boolean anyInterfaceSupportsIpV6() { - try { - Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); - while (interfaces.hasMoreElements()) { - NetworkInterface networkInterface = interfaces.nextElement(); - Enumeration addresses = networkInterface.getInetAddresses(); - while (addresses.hasMoreElements()) { - InetAddress inetAddress = addresses.nextElement(); - if (inetAddress instanceof Inet6Address - && !inetAddress.isAnyLocalAddress() - && !inetAddress.isLoopbackAddress() - && !inetAddress.isLinkLocalAddress()) { - return true; - } - } - } - } catch (SocketException ignore) { - if (LOGGER.isLoggable(System.Logger.Level.INFO)) { - LOGGER.log(System.Logger.Level.INFO, - "Unable to detect if any interface supports IPv6, assuming IPv4-only", - ignore); - } - } - return false; - } - } diff --git a/webclient/api/src/main/java/io/helidon/webclient/api/TcpClientConnection.java b/webclient/api/src/main/java/io/helidon/webclient/api/TcpClientConnection.java index b42af0f4414..3dd1d61b74c 100644 --- a/webclient/api/src/main/java/io/helidon/webclient/api/TcpClientConnection.java +++ b/webclient/api/src/main/java/io/helidon/webclient/api/TcpClientConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Oracle and/or its affiliates. + * Copyright (c) 2023, 2024 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,16 @@ package io.helidon.webclient.api; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; import java.io.IOException; import java.io.UncheckedIOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; +import java.net.UnknownHostException; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.time.Duration; @@ -30,10 +34,6 @@ import java.util.function.Consumer; import java.util.function.Function; -import javax.net.ssl.SSLPeerUnverifiedException; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocket; - import io.helidon.common.buffers.BufferData; import io.helidon.common.buffers.DataReader; import io.helidon.common.buffers.DataWriter; @@ -246,7 +246,16 @@ private String createChannelId(Socket socket) { private InetSocketAddress inetSocketAddress() { DnsResolver dnsResolver = connectionKey.dnsResolver(); if (dnsResolver.useDefaultJavaResolver()) { - return new InetSocketAddress(connectionKey.host(), connectionKey.port()); + try { + InetAddress[] addresses = InetAddress.getAllByName(connectionKey.host()); + addresses = connectionKey.dnsAddressLookup().filter(addresses); + if (addresses.length > 0) { + return new InetSocketAddress(addresses[0], connectionKey.port()); + } + } catch (UnknownHostException e) { + // falls through + } + throw new IllegalArgumentException("Failed to get address from host: " + connectionKey.host()); } else { InetAddress address = dnsResolver.resolveAddress(connectionKey.host(), connectionKey.dnsAddressLookup()); return new InetSocketAddress(address, connectionKey.port()); diff --git a/webclient/api/src/test/java/io/helidon/webclient/api/Ipv4LookupFinderTest.java b/webclient/api/src/test/java/io/helidon/webclient/api/Ipv4LookupFinderTest.java new file mode 100644 index 00000000000..01eb06d4f51 --- /dev/null +++ b/webclient/api/src/test/java/io/helidon/webclient/api/Ipv4LookupFinderTest.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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. + */ +package io.helidon.webclient.api; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +class Ipv4LookupFinderTest { + + /** + * Default is to prefer IPv4 addresses. + */ + @Test + void testIpv4Preference() { + DnsAddressLookup dnsAddressLookup = DefaultAddressLookupFinder.defaultDnsAddressLookup(); + assertThat(dnsAddressLookup, is(DnsAddressLookup.IPV4_PREFERRED)); + } +} diff --git a/webclient/api/src/test/java/io/helidon/webclient/api/Ipv6LookupFinderTest.java b/webclient/api/src/test/java/io/helidon/webclient/api/Ipv6LookupFinderTest.java new file mode 100644 index 00000000000..2501c138b54 --- /dev/null +++ b/webclient/api/src/test/java/io/helidon/webclient/api/Ipv6LookupFinderTest.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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. + */ +package io.helidon.webclient.api; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.nullValue; + +class Ipv6LookupFinderTest { + + /** + * If {@code java.net.preferIPv6Addresses} is set, use IPv6. + */ + @Test + void testIpv6Preference() { + assertThat(System.getProperty("java.net.preferIPv6Addresses"), is(nullValue())); + System.setProperty("java.net.preferIPv6Addresses", "true"); + DnsAddressLookup dnsAddressLookup = DefaultAddressLookupFinder.defaultDnsAddressLookup(); + assertThat(dnsAddressLookup, is(DnsAddressLookup.IPV6_PREFERRED)); + } +}