Skip to content

Commit 1db5680

Browse files
committed
Support Keep alive probes in Java 11+
JAVA-3656
1 parent 64bc673 commit 1db5680

File tree

2 files changed

+41
-0
lines changed

2 files changed

+41
-0
lines changed

driver-core/src/main/com/mongodb/internal/connection/SocketStreamHelper.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,39 @@
1919
import com.mongodb.MongoInternalException;
2020
import com.mongodb.connection.SocketSettings;
2121
import com.mongodb.connection.SslSettings;
22+
import jdk.net.ExtendedSocketOptions;
2223

2324
import javax.net.ssl.SSLParameters;
2425
import javax.net.ssl.SSLSocket;
2526
import java.io.IOException;
27+
import java.lang.reflect.Method;
2628
import java.net.InetSocketAddress;
2729
import java.net.Socket;
30+
import java.net.SocketOption;
31+
import java.util.Arrays;
2832

2933
import static com.mongodb.internal.connection.SslHelper.enableHostNameVerification;
3034
import static com.mongodb.internal.connection.SslHelper.enableSni;
3135
import static java.util.concurrent.TimeUnit.MILLISECONDS;
3236

3337
final class SocketStreamHelper {
38+
// Keep alive options and their values for Java 11+
39+
private static final String TCP_KEEPIDLE = "TCP_KEEPIDLE";
40+
private static final int TCP_KEEPIDLE_DURATION = 300;
41+
private static final String TCP_KEEPCOUNT = "TCP_KEEPCOUNT";
42+
private static final int TCP_KEEPCOUNT_LIMIT = 9;
43+
private static final String TCP_KEEPINTERVAL = "TCP_KEEPINTERVAL";
44+
private static final int TCP_KEEPINTERVAL_DURATION = 10;
3445

3546
static void initialize(final Socket socket, final InetSocketAddress inetSocketAddress, final SocketSettings settings,
3647
final SslSettings sslSettings) throws IOException {
3748
socket.setTcpNoDelay(true);
3849
socket.setSoTimeout(settings.getReadTimeout(MILLISECONDS));
3950
socket.setKeepAlive(true);
51+
52+
// Adding keep alive options for users of Java 11+. These options will be ignored for older Java versions.
53+
setExtendedSocketOptions(socket);
54+
4055
if (settings.getReceiveBufferSize() > 0) {
4156
socket.setReceiveBufferSize(settings.getReceiveBufferSize());
4257
}
@@ -63,6 +78,22 @@ static void initialize(final Socket socket, final InetSocketAddress inetSocketAd
6378
socket.connect(inetSocketAddress, settings.getConnectTimeout(MILLISECONDS));
6479
}
6580

81+
@SuppressWarnings("unchecked")
82+
private static void setExtendedSocketOptions(final Socket socket) {
83+
if (Arrays.stream(ExtendedSocketOptions.class.getDeclaredFields()).anyMatch(f -> f.getName().equals(TCP_KEEPCOUNT))) {
84+
try {
85+
Method setOptionMethod = Socket.class.getMethod("setOption", SocketOption.class, Object.class);
86+
setOptionMethod.invoke(socket, ExtendedSocketOptions.class.getDeclaredField(TCP_KEEPCOUNT).get(null),
87+
TCP_KEEPCOUNT_LIMIT);
88+
setOptionMethod.invoke(socket, ExtendedSocketOptions.class.getDeclaredField(TCP_KEEPIDLE).get(null),
89+
TCP_KEEPIDLE_DURATION);
90+
setOptionMethod.invoke(socket, ExtendedSocketOptions.class.getDeclaredField(TCP_KEEPINTERVAL).get(null),
91+
TCP_KEEPINTERVAL_DURATION);
92+
} catch (Throwable t) {
93+
}
94+
}
95+
}
96+
6697
private SocketStreamHelper() {
6798
}
6899
}

driver-core/src/test/functional/com/mongodb/internal/connection/SocketStreamHelperSpecification.groovy

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ import com.mongodb.ClusterFixture
2020
import com.mongodb.MongoInternalException
2121
import com.mongodb.connection.SocketSettings
2222
import com.mongodb.connection.SslSettings
23+
import jdk.net.ExtendedSocketOptions
2324
import spock.lang.IgnoreIf
2425
import spock.lang.Specification
2526

2627
import javax.net.SocketFactory
2728
import javax.net.ssl.SNIHostName
2829
import javax.net.ssl.SSLSocket
2930
import javax.net.ssl.SSLSocketFactory
31+
import java.lang.reflect.Method
3032

3133
import static com.mongodb.ClusterFixture.getPrimary
3234
import static java.util.concurrent.TimeUnit.MILLISECONDS
@@ -49,6 +51,14 @@ class SocketStreamHelperSpecification extends Specification {
4951
socket.getKeepAlive()
5052
socket.getSoTimeout() == socketSettings.getReadTimeout(MILLISECONDS)
5153
54+
// If the Java 11+ extended socket options for keep alive probes are available, check those values.
55+
if (Arrays.stream(ExtendedSocketOptions.getDeclaredFields()).anyMatch{ f -> f.getName().equals('TCP_KEEPCOUNT') }) {
56+
Method getOptionMethod = Socket.getMethod('getOption', SocketOption);
57+
getOptionMethod.invoke(socket, ExtendedSocketOptions.getDeclaredField('TCP_KEEPCOUNT').get(null)) == 9
58+
getOptionMethod.invoke(socket, ExtendedSocketOptions.getDeclaredField('TCP_KEEPIDLE').get(null)) == 300
59+
getOptionMethod.invoke(socket, ExtendedSocketOptions.getDeclaredField('TCP_KEEPINTERVAL').get(null)) == 10
60+
}
61+
5262
cleanup:
5363
socket?.close()
5464
}

0 commit comments

Comments
 (0)