From 95f3a08c20df43759061a780d2285e74a9c1aed2 Mon Sep 17 00:00:00 2001 From: Ryan Schmitt Date: Wed, 6 Aug 2025 13:02:28 -0700 Subject: [PATCH] Enable new TcpKeepAlive options on Java 8 This change sets the new KeepAlive-related options directly through the JDK, instead of going through `SocketSupport`. There are two reasons for this. First, the `SocketSupport` code path doesn't work on Java 8, as it attempts to reflectively call `Socket.setOption()`, which was added in newer versions. Second, the options themselves _do_ exist on Java 8. The Java socket options were backported to Java 8 in 8u261 (July 2020): https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8194298 As for Windows, it supports `TCP_KEEPIDLE` and `TCP_KEEPINTVL` since Windows 10 version 1709 (October 2017): https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-tcp-socket-options I think it's safe at this point to simply use these features like any other socket option. Unfortunately, it doesn't look like Mojo signatures are distributed for newer maintenance releases of Java 8, so I've had to update `hc.animal-sniffer.signature.ignores`. Finally, I've added test coverage in TestTlsHandshakeTimeout to exercise this feature, as well as to assert that KeepAlive doesn't interfere with socket timeouts. --- .../hc/client5/testing/sync/TestTlsHandshakeTimeout.java | 6 ++++++ .../impl/io/DefaultHttpClientConnectionOperator.java | 9 +++++---- pom.xml | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestTlsHandshakeTimeout.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestTlsHandshakeTimeout.java index c15d9e0cef..b763a71f50 100644 --- a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestTlsHandshakeTimeout.java +++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestTlsHandshakeTimeout.java @@ -42,6 +42,7 @@ import org.apache.hc.client5.testing.SSLTestContexts; import org.apache.hc.client5.testing.tls.TlsHandshakeTimeoutServer; import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.io.SocketConfig; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -71,6 +72,11 @@ void testTimeout(final boolean sendServerHello) throws Exception { .setConnectTimeout(5, SECONDS) .setSocketTimeout(5, SECONDS) .build()) + .setDefaultSocketConfig(SocketConfig.custom() + .setTcpKeepIdle(2) + .setTcpKeepInterval(1) + .setTcpKeepCount(5) + .build()) .setTlsSocketStrategy(new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext(), HostnameVerificationPolicy.CLIENT, NoopHostnameVerifier.INSTANCE)) .setDefaultTlsConfig(TlsConfig.custom() .setHandshakeTimeout(EXPECTED_TIMEOUT.toMillis(), MILLISECONDS) diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultHttpClientConnectionOperator.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultHttpClientConnectionOperator.java index 74a827e532..19edf193c0 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultHttpClientConnectionOperator.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultHttpClientConnectionOperator.java @@ -36,6 +36,8 @@ import java.util.Collections; import java.util.List; +import jdk.net.ExtendedSocketOptions; +import jdk.net.Sockets; import org.apache.hc.client5.http.ConnectExceptionSupport; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.SchemePortResolver; @@ -58,7 +60,6 @@ import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.Closer; -import org.apache.hc.core5.io.SocketSupport; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; @@ -318,13 +319,13 @@ private static void configureSocket(final Socket socket, final SocketConfig sock socket.setSoLinger(true, linger); } if (socketConfig.getTcpKeepIdle() > 0) { - SocketSupport.setOption(socket, SocketSupport.TCP_KEEPIDLE, socketConfig.getTcpKeepIdle()); + Sockets.setOption(socket, ExtendedSocketOptions.TCP_KEEPIDLE, socketConfig.getTcpKeepIdle()); } if (socketConfig.getTcpKeepInterval() > 0) { - SocketSupport.setOption(socket, SocketSupport.TCP_KEEPINTERVAL, socketConfig.getTcpKeepInterval()); + Sockets.setOption(socket, ExtendedSocketOptions.TCP_KEEPINTERVAL, socketConfig.getTcpKeepInterval()); } if (socketConfig.getTcpKeepCount() > 0) { - SocketSupport.setOption(socket, SocketSupport.TCP_KEEPCOUNT, socketConfig.getTcpKeepCount()); + Sockets.setOption(socket, ExtendedSocketOptions.TCP_KEEPCOUNT, socketConfig.getTcpKeepCount()); } } diff --git a/pom.xml b/pom.xml index e15b97926f..d841a29aa3 100644 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,7 @@ 1.21.3 2.10.1 5.3 - javax.net.ssl.SSLEngine,javax.net.ssl.SSLParameters,java.nio.ByteBuffer,java.nio.CharBuffer + javax.net.ssl.SSLEngine,javax.net.ssl.SSLParameters,java.nio.ByteBuffer,java.nio.CharBuffer,jdk.net.ExtendedSocketOptions,jdk.net.Sockets 1.27.1 1.5.7-4