Skip to content

Commit cc9b692

Browse files
committed
Check for keep-alive option support
It turns out that keep-alive options are supported on all modern combinations of Java runtimes and operating systems, with the sole excpetion of Java 8 on Windows. The keep-alive options are specifically unavailable on this combination: you can refer to them in code, but attempting to set them will result in an UnsupportedOperationException. The good news is that there's a method we can call, `Sockets.supportedOptions()`, that returns a complete list of socket options supported by the current runtime. This change simply uses that functionality to check for support before setting keep-alive options on any socket.
1 parent 49f1827 commit cc9b692

File tree

5 files changed

+61
-34
lines changed

5 files changed

+61
-34
lines changed

httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/HttpRequester.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
import org.apache.hc.core5.pool.PoolEntry;
8585
import org.apache.hc.core5.pool.PoolStats;
8686
import org.apache.hc.core5.util.Args;
87+
import org.apache.hc.core5.util.ReflectionUtils;
8788
import org.apache.hc.core5.util.TimeValue;
8889
import org.apache.hc.core5.util.Timeout;
8990

@@ -240,6 +241,7 @@ public <T> T execute(
240241
}
241242
}
242243

244+
@SuppressWarnings("Since15")
243245
private HttpClientConnection createConnection(final Socket sock, final HttpHost targetHost) throws IOException {
244246
sock.setSoTimeout(socketConfig.getSoTimeout().toMillisecondsIntBound());
245247
sock.setReuseAddress(socketConfig.isSoReuseAddress());
@@ -251,14 +253,16 @@ private HttpClientConnection createConnection(final Socket sock, final HttpHost
251253
if (socketConfig.getSndBufSize() > 0) {
252254
sock.setSendBufferSize(socketConfig.getSndBufSize());
253255
}
254-
if (this.socketConfig.getTcpKeepIdle() > 0) {
255-
Sockets.setOption(sock, ExtendedSocketOptions.TCP_KEEPIDLE, this.socketConfig.getTcpKeepIdle());
256-
}
257-
if (this.socketConfig.getTcpKeepInterval() > 0) {
258-
Sockets.setOption(sock, ExtendedSocketOptions.TCP_KEEPINTERVAL, this.socketConfig.getTcpKeepInterval());
259-
}
260-
if (this.socketConfig.getTcpKeepCount() > 0) {
261-
Sockets.setOption(sock, ExtendedSocketOptions.TCP_KEEPCOUNT, this.socketConfig.getTcpKeepCount());
256+
if (ReflectionUtils.supportsKeepAliveOptions()) {
257+
if (this.socketConfig.getTcpKeepIdle() > 0) {
258+
Sockets.setOption(sock, ExtendedSocketOptions.TCP_KEEPIDLE, this.socketConfig.getTcpKeepIdle());
259+
}
260+
if (this.socketConfig.getTcpKeepInterval() > 0) {
261+
Sockets.setOption(sock, ExtendedSocketOptions.TCP_KEEPINTERVAL, this.socketConfig.getTcpKeepInterval());
262+
}
263+
if (this.socketConfig.getTcpKeepCount() > 0) {
264+
Sockets.setOption(sock, ExtendedSocketOptions.TCP_KEEPCOUNT, this.socketConfig.getTcpKeepCount());
265+
}
262266
}
263267
final int linger = socketConfig.getSoLinger().toMillisecondsIntBound();
264268
if (linger >= 0) {

httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/HttpServer.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import org.apache.hc.core5.io.Closer;
6060
import org.apache.hc.core5.io.ModalCloseable;
6161
import org.apache.hc.core5.util.Args;
62+
import org.apache.hc.core5.util.ReflectionUtils;
6263
import org.apache.hc.core5.util.TimeValue;
6364
import org.apache.hc.core5.util.Timeout;
6465

@@ -139,6 +140,7 @@ public int getLocalPort() {
139140
return -1;
140141
}
141142

143+
@SuppressWarnings("Since15")
142144
public void start() throws IOException {
143145
if (this.status.compareAndSet(Status.READY, Status.ACTIVE)) {
144146
this.serverSocket = this.serverSocketFactory.createServerSocket(
@@ -147,14 +149,16 @@ public void start() throws IOException {
147149
if (this.socketConfig.getRcvBufSize() > 0) {
148150
this.serverSocket.setReceiveBufferSize(this.socketConfig.getRcvBufSize());
149151
}
150-
if (this.socketConfig.getTcpKeepIdle() > 0) {
151-
Sockets.setOption(this.serverSocket, ExtendedSocketOptions.TCP_KEEPIDLE, this.socketConfig.getTcpKeepIdle());
152-
}
153-
if (this.socketConfig.getTcpKeepInterval() > 0) {
154-
Sockets.setOption(this.serverSocket, ExtendedSocketOptions.TCP_KEEPINTERVAL, this.socketConfig.getTcpKeepInterval());
155-
}
156-
if (this.socketConfig.getTcpKeepCount() > 0) {
157-
Sockets.setOption(this.serverSocket, ExtendedSocketOptions.TCP_KEEPCOUNT, this.socketConfig.getTcpKeepCount());
152+
if (ReflectionUtils.supportsKeepAliveOptions()) {
153+
if (this.socketConfig.getTcpKeepIdle() > 0) {
154+
Sockets.setOption(this.serverSocket, ExtendedSocketOptions.TCP_KEEPIDLE, this.socketConfig.getTcpKeepIdle());
155+
}
156+
if (this.socketConfig.getTcpKeepInterval() > 0) {
157+
Sockets.setOption(this.serverSocket, ExtendedSocketOptions.TCP_KEEPINTERVAL, this.socketConfig.getTcpKeepInterval());
158+
}
159+
if (this.socketConfig.getTcpKeepCount() > 0) {
160+
Sockets.setOption(this.serverSocket, ExtendedSocketOptions.TCP_KEEPCOUNT, this.socketConfig.getTcpKeepCount());
161+
}
158162
}
159163
if (this.sslSetupHandler != null && this.serverSocket instanceof SSLServerSocket) {
160164
final SSLServerSocket sslServerSocket = (SSLServerSocket) this.serverSocket;

httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/RequestListener.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.apache.hc.core5.http.io.HttpServerConnection;
4848
import org.apache.hc.core5.http.io.SocketConfig;
4949
import org.apache.hc.core5.io.Closer;
50+
import org.apache.hc.core5.util.ReflectionUtils;
5051

5152
class RequestListener implements Runnable {
5253

@@ -80,6 +81,7 @@ public RequestListener(
8081
this.terminated = new AtomicBoolean();
8182
}
8283

84+
@SuppressWarnings("Since15")
8385
private HttpServerConnection createConnection(final Socket socket) throws IOException {
8486
socket.setSoTimeout(this.socketConfig.getSoTimeout().toMillisecondsIntBound());
8587
socket.setKeepAlive(this.socketConfig.isSoKeepAlive());
@@ -93,14 +95,16 @@ private HttpServerConnection createConnection(final Socket socket) throws IOExce
9395
if (this.socketConfig.getSoLinger().toSeconds() >= 0) {
9496
socket.setSoLinger(true, this.socketConfig.getSoLinger().toSecondsIntBound());
9597
}
96-
if (this.socketConfig.getTcpKeepIdle() > 0) {
97-
Sockets.setOption(this.serverSocket, ExtendedSocketOptions.TCP_KEEPIDLE, this.socketConfig.getTcpKeepIdle());
98-
}
99-
if (this.socketConfig.getTcpKeepInterval() > 0) {
100-
Sockets.setOption(this.serverSocket, ExtendedSocketOptions.TCP_KEEPINTERVAL, this.socketConfig.getTcpKeepInterval());
101-
}
102-
if (this.socketConfig.getTcpKeepCount() > 0) {
103-
Sockets.setOption(this.serverSocket, ExtendedSocketOptions.TCP_KEEPCOUNT, this.socketConfig.getTcpKeepCount());
98+
if (ReflectionUtils.supportsKeepAliveOptions()) {
99+
if (this.socketConfig.getTcpKeepIdle() > 0) {
100+
Sockets.setOption(this.serverSocket, ExtendedSocketOptions.TCP_KEEPIDLE, this.socketConfig.getTcpKeepIdle());
101+
}
102+
if (this.socketConfig.getTcpKeepInterval() > 0) {
103+
Sockets.setOption(this.serverSocket, ExtendedSocketOptions.TCP_KEEPINTERVAL, this.socketConfig.getTcpKeepInterval());
104+
}
105+
if (this.socketConfig.getTcpKeepCount() > 0) {
106+
Sockets.setOption(this.serverSocket, ExtendedSocketOptions.TCP_KEEPCOUNT, this.socketConfig.getTcpKeepCount());
107+
}
104108
}
105109
if (!(socket instanceof SSLSocket) && sslSocketFactory != null) {
106110
final SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(socket, null, -1, false);

httpcore5/src/main/java/org/apache/hc/core5/reactor/SingleCoreIOReactor.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.apache.hc.core5.io.Closer;
5656
import org.apache.hc.core5.net.NamedEndpoint;
5757
import org.apache.hc.core5.util.Args;
58+
import org.apache.hc.core5.util.ReflectionUtils;
5859
import org.apache.hc.core5.util.Timeout;
5960

6061
class SingleCoreIOReactor extends AbstractSingleCoreIOReactor implements ConnectionInitiator, IOWorkerStats {
@@ -278,6 +279,7 @@ public Future<IOSession> connect(
278279
return sessionRequest;
279280
}
280281

282+
@SuppressWarnings("Since15")
281283
private void prepareSocket(final SocketChannel socketChannel) throws IOException {
282284
if (this.reactorConfig.getSndBufSize() > 0) {
283285
socketChannel.setOption(StandardSocketOptions.SO_SNDBUF, this.reactorConfig.getSndBufSize());
@@ -300,14 +302,16 @@ private void prepareSocket(final SocketChannel socketChannel) throws IOException
300302
if (this.reactorConfig.getTrafficClass() > 0) {
301303
socketChannel.setOption(StandardSocketOptions.IP_TOS, this.reactorConfig.getTrafficClass());
302304
}
303-
if (this.reactorConfig.getTcpKeepIdle() > 0) {
304-
socketChannel.setOption(ExtendedSocketOptions.TCP_KEEPIDLE, this.reactorConfig.getTcpKeepIdle());
305-
}
306-
if (this.reactorConfig.getTcpKeepInterval() > 0) {
307-
socketChannel.setOption(ExtendedSocketOptions.TCP_KEEPINTERVAL, this.reactorConfig.getTcpKeepInterval());
308-
}
309-
if (this.reactorConfig.getTcpKeepCount() > 0) {
310-
socketChannel.setOption(ExtendedSocketOptions.TCP_KEEPCOUNT, this.reactorConfig.getTcpKeepCount());
305+
if (ReflectionUtils.supportsKeepAliveOptions()) {
306+
if (this.reactorConfig.getTcpKeepIdle() > 0) {
307+
socketChannel.setOption(ExtendedSocketOptions.TCP_KEEPIDLE, this.reactorConfig.getTcpKeepIdle());
308+
}
309+
if (this.reactorConfig.getTcpKeepInterval() > 0) {
310+
socketChannel.setOption(ExtendedSocketOptions.TCP_KEEPINTERVAL, this.reactorConfig.getTcpKeepInterval());
311+
}
312+
if (this.reactorConfig.getTcpKeepCount() > 0) {
313+
socketChannel.setOption(ExtendedSocketOptions.TCP_KEEPCOUNT, this.reactorConfig.getTcpKeepCount());
314+
}
311315
}
312316
}
313317

httpcore5/src/main/java/org/apache/hc/core5/util/ReflectionUtils.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,20 @@
2727

2828
package org.apache.hc.core5.util;
2929

30-
import java.lang.reflect.Method;
31-
30+
import jdk.net.ExtendedSocketOptions;
31+
import jdk.net.Sockets;
3232
import org.apache.hc.core5.annotation.Internal;
3333

34+
import java.lang.reflect.Method;
35+
import java.net.Socket;
36+
import java.util.Arrays;
37+
3438
@Internal
39+
@SuppressWarnings("Since15")
3540
public final class ReflectionUtils {
41+
private static final boolean SUPPORTS_KEEPALIVE_OPTIONS = Sockets.supportedOptions(Socket.class)
42+
.containsAll(Arrays.asList(ExtendedSocketOptions.TCP_KEEPIDLE, ExtendedSocketOptions.TCP_KEEPINTERVAL,
43+
ExtendedSocketOptions.TCP_KEEPCOUNT));
3644

3745
public static void callSetter(final Object object, final String setterName, final Class<?> type, final Object value) {
3846
try {
@@ -89,4 +97,7 @@ public static int determineJRELevel() {
8997
return 7;
9098
}
9199

100+
public static boolean supportsKeepAliveOptions() {
101+
return SUPPORTS_KEEPALIVE_OPTIONS;
102+
}
92103
}

0 commit comments

Comments
 (0)