diff --git a/vertx-core/src/main/generated/io/vertx/core/net/TCPSSLOptionsConverter.java b/vertx-core/src/main/generated/io/vertx/core/net/TCPSSLOptionsConverter.java index 220b7886f37..05b209c1787 100644 --- a/vertx-core/src/main/generated/io/vertx/core/net/TCPSSLOptionsConverter.java +++ b/vertx-core/src/main/generated/io/vertx/core/net/TCPSSLOptionsConverter.java @@ -113,6 +113,21 @@ static void fromJson(Iterable> json, TCPSSLO obj.setTcpUserTimeout(((Number)member.getValue()).intValue()); } break; + case "tcpKeepAliveIdleSeconds": + if (member.getValue() instanceof Number) { + obj.setTcpKeepAliveIdleSeconds(((Number)member.getValue()).intValue()); + } + break; + case "tcpKeepAliveCount": + if (member.getValue() instanceof Number) { + obj.setTcpKeepAliveCount(((Number)member.getValue()).intValue()); + } + break; + case "tcpKeepAliveIntervalSeconds": + if (member.getValue() instanceof Number) { + obj.setTcpKeepAliveIntervalSeconds(((Number)member.getValue()).intValue()); + } + break; case "sslHandshakeTimeout": if (member.getValue() instanceof Number) { obj.setSslHandshakeTimeout(((Number)member.getValue()).longValue()); @@ -167,6 +182,9 @@ static void toJson(TCPSSLOptions obj, java.util.Map json) { json.put("tcpCork", obj.isTcpCork()); json.put("tcpQuickAck", obj.isTcpQuickAck()); json.put("tcpUserTimeout", obj.getTcpUserTimeout()); + json.put("tcpKeepAliveIdleSeconds", obj.getTcpKeepAliveIdleSeconds()); + json.put("tcpKeepAliveCount", obj.getTcpKeepAliveCount()); + json.put("tcpKeepAliveIntervalSeconds", obj.getTcpKeepAliveIntervalSeconds()); json.put("sslHandshakeTimeout", obj.getSslHandshakeTimeout()); if (obj.getSslHandshakeTimeoutUnit() != null) { json.put("sslHandshakeTimeoutUnit", obj.getSslHandshakeTimeoutUnit().name()); diff --git a/vertx-core/src/main/java/io/vertx/core/impl/transports/EpollTransport.java b/vertx-core/src/main/java/io/vertx/core/impl/transports/EpollTransport.java index 77744c12e59..b3ae3eaf1f0 100644 --- a/vertx-core/src/main/java/io/vertx/core/impl/transports/EpollTransport.java +++ b/vertx-core/src/main/java/io/vertx/core/impl/transports/EpollTransport.java @@ -136,6 +136,9 @@ public void configure(NetServerOptions options, boolean domainSocket, ServerBoot bootstrap.childOption(EpollChannelOption.TCP_USER_TIMEOUT, options.getTcpUserTimeout()); bootstrap.childOption(EpollChannelOption.TCP_QUICKACK, options.isTcpQuickAck()); bootstrap.childOption(EpollChannelOption.TCP_CORK, options.isTcpCork()); + bootstrap.childOption(EpollChannelOption.TCP_KEEPIDLE, options.getTcpKeepAliveIdleSeconds()); + bootstrap.childOption(EpollChannelOption.TCP_KEEPCNT, options.getTcpKeepAliveCount()); + bootstrap.childOption(EpollChannelOption.TCP_KEEPINTVL, options.getTcpKeepAliveIntervalSeconds()); } Transport.super.configure(options, domainSocket, bootstrap); } @@ -149,6 +152,9 @@ public void configure(ClientOptionsBase options, int connectTimeout, boolean dom bootstrap.option(EpollChannelOption.TCP_USER_TIMEOUT, options.getTcpUserTimeout()); bootstrap.option(EpollChannelOption.TCP_QUICKACK, options.isTcpQuickAck()); bootstrap.option(EpollChannelOption.TCP_CORK, options.isTcpCork()); + bootstrap.option(EpollChannelOption.TCP_KEEPIDLE, options.getTcpKeepAliveIdleSeconds()); + bootstrap.option(EpollChannelOption.TCP_KEEPCNT, options.getTcpKeepAliveCount()); + bootstrap.option(EpollChannelOption.TCP_KEEPINTVL, options.getTcpKeepAliveIntervalSeconds()); } Transport.super.configure(options, connectTimeout, domainSocket, bootstrap); } diff --git a/vertx-core/src/main/java/io/vertx/core/impl/transports/IoUringTransport.java b/vertx-core/src/main/java/io/vertx/core/impl/transports/IoUringTransport.java index ff0be012d0f..aab15cf6784 100644 --- a/vertx-core/src/main/java/io/vertx/core/impl/transports/IoUringTransport.java +++ b/vertx-core/src/main/java/io/vertx/core/impl/transports/IoUringTransport.java @@ -141,6 +141,9 @@ public void configure(NetServerOptions options, boolean domainSocket, ServerBoot bootstrap.childOption(IoUringChannelOption.TCP_USER_TIMEOUT, options.getTcpUserTimeout()); bootstrap.childOption(IoUringChannelOption.TCP_QUICKACK, options.isTcpQuickAck()); bootstrap.childOption(IoUringChannelOption.TCP_CORK, options.isTcpCork()); + bootstrap.childOption(IoUringChannelOption.TCP_KEEPIDLE, options.getTcpKeepAliveIdleSeconds()); + bootstrap.childOption(IoUringChannelOption.TCP_KEEPCNT, options.getTcpKeepAliveCount()); + bootstrap.childOption(IoUringChannelOption.TCP_KEEPINTVL, options.getTcpKeepAliveIntervalSeconds()); Transport.super.configure(options, false, bootstrap); } @@ -155,6 +158,9 @@ public void configure(ClientOptionsBase options, int connectTimeout, boolean dom bootstrap.option(IoUringChannelOption.TCP_USER_TIMEOUT, options.getTcpUserTimeout()); bootstrap.option(IoUringChannelOption.TCP_QUICKACK, options.isTcpQuickAck()); bootstrap.option(IoUringChannelOption.TCP_CORK, options.isTcpCork()); + bootstrap.option(IoUringChannelOption.TCP_KEEPIDLE, options.getTcpKeepAliveIdleSeconds()); + bootstrap.option(IoUringChannelOption.TCP_KEEPCNT, options.getTcpKeepAliveCount()); + bootstrap.option(IoUringChannelOption.TCP_KEEPINTVL, options.getTcpKeepAliveIntervalSeconds()); Transport.super.configure(options, connectTimeout, false, bootstrap); } } diff --git a/vertx-core/src/main/java/io/vertx/core/net/NetServerOptions.java b/vertx-core/src/main/java/io/vertx/core/net/NetServerOptions.java index 35ffd27dc3f..19c3c34fb3d 100755 --- a/vertx-core/src/main/java/io/vertx/core/net/NetServerOptions.java +++ b/vertx-core/src/main/java/io/vertx/core/net/NetServerOptions.java @@ -17,6 +17,7 @@ import io.vertx.codegen.json.annotations.JsonGen; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.ClientAuth; +import io.vertx.core.impl.Arguments; import io.vertx.core.json.JsonObject; import java.util.Set; @@ -88,7 +89,7 @@ public NetServerOptions() { /** * Copy constructor * - * @param other the options to copy + * @param other the options to copy */ public NetServerOptions(NetServerOptions other) { super(other); @@ -107,7 +108,7 @@ public NetServerOptions(NetServerOptions other) { /** * Create some options from JSON * - * @param json the JSON + * @param json the JSON */ public NetServerOptions(JsonObject json) { super(json); @@ -335,7 +336,6 @@ public NetServerOptions setAcceptBacklog(int acceptBacklog) { } /** - * * @return the port */ public int getPort() { @@ -345,7 +345,7 @@ public int getPort() { /** * Set the port * - * @param port the port + * @param port the port * @return a reference to this, so the API can be used fluently */ public NetServerOptions setPort(int port) { @@ -357,7 +357,6 @@ public NetServerOptions setPort(int port) { } /** - * * @return the host */ public String getHost() { @@ -366,7 +365,8 @@ public String getHost() { /** * Set the host - * @param host the host + * + * @param host the host * @return a reference to this, so the API can be used fluently */ public NetServerOptions setHost(String host) { @@ -423,7 +423,9 @@ public NetServerOptions setSni(boolean sni) { /** * @return whether the server uses the HA Proxy protocol */ - public boolean isUseProxyProtocol() { return useProxyProtocol; } + public boolean isUseProxyProtocol() { + return useProxyProtocol; + } /** * Set whether the server uses the HA Proxy protocol diff --git a/vertx-core/src/main/java/io/vertx/core/net/TCPSSLOptions.java b/vertx-core/src/main/java/io/vertx/core/net/TCPSSLOptions.java index 10558a5c688..d8195c664bc 100755 --- a/vertx-core/src/main/java/io/vertx/core/net/TCPSSLOptions.java +++ b/vertx-core/src/main/java/io/vertx/core/net/TCPSSLOptions.java @@ -15,6 +15,7 @@ import io.vertx.codegen.annotations.GenIgnore; import io.vertx.codegen.json.annotations.JsonGen; import io.vertx.core.buffer.Buffer; +import io.vertx.core.impl.Arguments; import io.vertx.core.json.JsonObject; import io.netty.handler.logging.ByteBufFormat; @@ -97,6 +98,27 @@ public abstract class TCPSSLOptions extends NetworkOptions { */ public static final int DEFAULT_TCP_USER_TIMEOUT = 0; + /** + * Default value for tcp keepalive idle time. + *

+ * {@code -1} defaults to OS settings + */ + public static final int DEFAULT_TCP_KEEPALIVE_IDLE_SECONDS = -1; + + /** + * Default value for tcp keepalive count. + *

+ * {@code -1} defaults to OS settings + */ + public static final int DEFAULT_TCP_KEEPALIVE_COUNT = -1; + + /** + * Default value for tcp keepalive interval. + *

+ * {@code -1} defaults to OS settings + */ + public static final int DEFAULT_TCP_KEEAPLIVE_INTERVAL_SECONDS = -1; + private boolean tcpNoDelay; private boolean tcpKeepAlive; private int soLinger; @@ -111,6 +133,9 @@ public abstract class TCPSSLOptions extends NetworkOptions { private boolean tcpCork; private boolean tcpQuickAck; private int tcpUserTimeout; + private int tcpKeepAliveIdleSeconds; + private int tcpKeepAliveCount; + private int tcpKeepAliveIntervalSeconds; private Set enabledCipherSuites; private List crlPaths; @@ -144,6 +169,9 @@ public TCPSSLOptions(TCPSSLOptions other) { this.tcpCork = other.isTcpCork(); this.tcpQuickAck = other.isTcpQuickAck(); this.tcpUserTimeout = other.getTcpUserTimeout(); + this.tcpKeepAliveIdleSeconds = other.getTcpKeepAliveIdleSeconds(); + this.tcpKeepAliveCount = other.getTcpKeepAliveCount(); + this.tcpKeepAliveIntervalSeconds = other.getTcpKeepAliveIntervalSeconds(); SSLOptions sslOptions = other.sslOptions; if (sslOptions != null) { @@ -240,6 +268,9 @@ private void init() { tcpCork = DEFAULT_TCP_CORK; tcpQuickAck = DEFAULT_TCP_QUICKACK; tcpUserTimeout = DEFAULT_TCP_USER_TIMEOUT; + tcpKeepAliveIdleSeconds = DEFAULT_TCP_KEEPALIVE_IDLE_SECONDS; + tcpKeepAliveCount = DEFAULT_TCP_KEEPALIVE_COUNT; + tcpKeepAliveIntervalSeconds = DEFAULT_TCP_KEEAPLIVE_INTERVAL_SECONDS; sslOptions = null; } @@ -704,6 +735,62 @@ public TCPSSLOptions setTcpUserTimeout(int tcpUserTimeout) { return this; } + /** + * @return the time in seconds the connection needs to remain idle before TCP starts sending keepalive probes + */ + public int getTcpKeepAliveIdleSeconds() { + return tcpKeepAliveIdleSeconds; + } + + /** + * The time in seconds the connection needs to remain idle before TCP starts sending keepalive probes, + * if the socket option keepalive has been set. + * + * @param tcpKeepAliveIdleSeconds + * @return a reference to this, so the API can be used fluently + */ + public TCPSSLOptions setTcpKeepAliveIdleSeconds(int tcpKeepAliveIdleSeconds) { + Arguments.require(tcpKeepAliveIdleSeconds > 0 || tcpKeepAliveIdleSeconds == DEFAULT_TCP_KEEPALIVE_IDLE_SECONDS, "tcpKeepAliveIdleSeconds must be > 0"); + this.tcpKeepAliveIdleSeconds = tcpKeepAliveIdleSeconds; + return this; + } + + /** + * @return the maximum number of keepalive probes TCP should send before dropping the connection. + */ + public int getTcpKeepAliveCount() { + return tcpKeepAliveCount; + } + + /** + * The maximum number of keepalive probes TCP should send before dropping the connection. + * @param tcpKeepAliveCount + * @return a reference to this, so the API can be used fluently + */ + public TCPSSLOptions setTcpKeepAliveCount(int tcpKeepAliveCount) { + Arguments.require(tcpKeepAliveCount > 0 || tcpKeepAliveCount == DEFAULT_TCP_KEEPALIVE_COUNT, "tcpKeepAliveCount must be > 0"); + this.tcpKeepAliveCount = tcpKeepAliveCount; + return this; + } + + /** + * @return the time in seconds between individual keepalive probes. + */ + public int getTcpKeepAliveIntervalSeconds() { + return tcpKeepAliveIntervalSeconds; + } + + /** + * The time in seconds between individual keepalive probes. + * @param tcpKeepAliveIntervalSeconds + * @return + */ + public TCPSSLOptions setTcpKeepAliveIntervalSeconds(int tcpKeepAliveIntervalSeconds) { + Arguments.require(tcpKeepAliveIntervalSeconds > 0 || tcpKeepAliveIntervalSeconds == DEFAULT_TCP_KEEAPLIVE_INTERVAL_SECONDS, "tcpKeepAliveIntervalSeconds must be > 0"); + this.tcpKeepAliveIntervalSeconds = tcpKeepAliveIntervalSeconds; + return this; + } + /** * Returns the enabled SSL/TLS protocols * @return the enabled protocols diff --git a/vertx-core/src/main/java/io/vertx/core/spi/transport/Transport.java b/vertx-core/src/main/java/io/vertx/core/spi/transport/Transport.java index 8eed081b06a..6bfe95c388a 100644 --- a/vertx-core/src/main/java/io/vertx/core/spi/transport/Transport.java +++ b/vertx-core/src/main/java/io/vertx/core/spi/transport/Transport.java @@ -14,6 +14,7 @@ import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; +import io.netty.channel.epoll.EpollChannelOption; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.InternetProtocolFamily; import io.vertx.core.datagram.DatagramSocketOptions; @@ -82,11 +83,10 @@ default io.vertx.core.net.SocketAddress convert(SocketAddress address) { IoHandlerFactory ioHandlerFactory(); /** - * @param type one of {@link #ACCEPTOR_EVENT_LOOP_GROUP} or {@link #IO_EVENT_LOOP_GROUP}. - * @param nThreads the number of threads that will be used by this instance. + * @param type one of {@link #ACCEPTOR_EVENT_LOOP_GROUP} or {@link #IO_EVENT_LOOP_GROUP}. + * @param nThreads the number of threads that will be used by this instance. * @param threadFactory the ThreadFactory to use. - * @param ioRatio the IO ratio - * + * @param ioRatio the IO ratio * @return a new event loop group */ default EventLoopGroup eventLoopGroup(int type, int nThreads, ThreadFactory threadFactory, int ioRatio) { @@ -104,14 +104,14 @@ default EventLoopGroup eventLoopGroup(int type, int nThreads, ThreadFactory thre DatagramChannel datagramChannel(InternetProtocolFamily family); /** - * @return the type for channel * @param domainSocket whether to create a unix domain channel or a socket channel + * @return the type for channel */ ChannelFactory channelFactory(boolean domainSocket); /** - * @return the type for server channel * @param domainSocket whether to create a server unix domain channel or a regular server socket channel + * @return the type for server channel */ ChannelFactory serverChannelFactory(boolean domainSocket); diff --git a/vertx-core/src/test/java/io/vertx/tests/net/NetTest.java b/vertx-core/src/test/java/io/vertx/tests/net/NetTest.java index 9152f414836..7bc53a614c2 100755 --- a/vertx-core/src/test/java/io/vertx/tests/net/NetTest.java +++ b/vertx-core/src/test/java/io/vertx/tests/net/NetTest.java @@ -274,6 +274,27 @@ public void testClientOptions() { assertEquals(randLong, options.getSslHandshakeTimeout()); assertIllegalArgumentException(() -> options.setSslHandshakeTimeout(-123)); + assertEquals(NetClientOptions.DEFAULT_TCP_KEEPALIVE_IDLE_SECONDS, options.getTcpKeepAliveIdleSeconds()); + rand = TestUtils.randomPositiveInt(); + assertEquals(options, options.setTcpKeepAliveIdleSeconds(rand)); + assertEquals(rand, options.getTcpKeepAliveIdleSeconds()); + assertIllegalArgumentException(() -> options.setTcpKeepAliveIdleSeconds(0)); + assertIllegalArgumentException(() -> options.setTcpKeepAliveIdleSeconds(-123)); + + assertEquals(NetClientOptions.DEFAULT_TCP_KEEPALIVE_COUNT, options.getTcpKeepAliveCount()); + rand = TestUtils.randomPositiveInt(); + assertEquals(options, options.setTcpKeepAliveCount(rand)); + assertEquals(rand, options.getTcpKeepAliveCount()); + assertIllegalArgumentException(() -> options.setTcpKeepAliveCount(0)); + assertIllegalArgumentException(() -> options.setTcpKeepAliveCount(-123)); + + assertEquals(NetClientOptions.DEFAULT_TCP_KEEAPLIVE_INTERVAL_SECONDS, options.getTcpKeepAliveIntervalSeconds()); + rand = TestUtils.randomPositiveInt(); + assertEquals(options, options.setTcpKeepAliveIntervalSeconds(rand)); + assertEquals(rand, options.getTcpKeepAliveIntervalSeconds()); + assertIllegalArgumentException(() -> options.setTcpKeepAliveIntervalSeconds(0)); + assertIllegalArgumentException(() -> options.setTcpKeepAliveIntervalSeconds(-123)); + testComplete(); } @@ -398,6 +419,27 @@ public void testServerOptions() { assertEquals(randomProxyTimeout, options.getProxyProtocolTimeout()); assertIllegalArgumentException(() -> options.setProxyProtocolTimeout(-123)); + assertEquals(NetServerOptions.DEFAULT_TCP_KEEPALIVE_IDLE_SECONDS, options.getTcpKeepAliveIdleSeconds()); + rand = TestUtils.randomPositiveInt(); + assertEquals(options, options.setTcpKeepAliveIdleSeconds(rand)); + assertEquals(rand, options.getTcpKeepAliveIdleSeconds()); + assertIllegalArgumentException(() -> options.setTcpKeepAliveIdleSeconds(0)); + assertIllegalArgumentException(() -> options.setTcpKeepAliveIdleSeconds(-123)); + + assertEquals(NetServerOptions.DEFAULT_TCP_KEEPALIVE_COUNT, options.getTcpKeepAliveCount()); + rand = TestUtils.randomPositiveInt(); + assertEquals(options, options.setTcpKeepAliveCount(rand)); + assertEquals(rand, options.getTcpKeepAliveCount()); + assertIllegalArgumentException(() -> options.setTcpKeepAliveCount(0)); + assertIllegalArgumentException(() -> options.setTcpKeepAliveCount(-123)); + + assertEquals(NetServerOptions.DEFAULT_TCP_KEEAPLIVE_INTERVAL_SECONDS, options.getTcpKeepAliveIntervalSeconds()); + rand = TestUtils.randomPositiveInt(); + assertEquals(options, options.setTcpKeepAliveIntervalSeconds(rand)); + assertEquals(rand, options.getTcpKeepAliveIntervalSeconds()); + assertIllegalArgumentException(() -> options.setTcpKeepAliveIntervalSeconds(0)); + assertIllegalArgumentException(() -> options.setTcpKeepAliveIntervalSeconds(-123)); + testComplete(); } @@ -650,6 +692,9 @@ public void testCopyServerOptions() { long sslHandshakeTimeout = TestUtils.randomPositiveLong(); boolean useProxyProtocol = TestUtils.randomBoolean(); long proxyProtocolTimeout = TestUtils.randomPositiveLong(); + int tcpKeepAliveIdleSeconds = TestUtils.randomPositiveInt(); + int tcpKeepAliveCount = TestUtils.randomPositiveInt(); + int tcpKeepAliveIntervalSeconds = TestUtils.randomPositiveInt(); options.setSendBufferSize(sendBufferSize); options.setReceiveBufferSize(receiverBufferSize); @@ -675,6 +720,9 @@ public void testCopyServerOptions() { options.setSslHandshakeTimeout(sslHandshakeTimeout); options.setUseProxyProtocol(useProxyProtocol); options.setProxyProtocolTimeout(proxyProtocolTimeout); + options.setTcpKeepAliveIdleSeconds(tcpKeepAliveIdleSeconds); + options.setTcpKeepAliveCount(tcpKeepAliveCount); + options.setTcpKeepAliveIntervalSeconds(tcpKeepAliveIntervalSeconds); NetServerOptions copy = new NetServerOptions(options); assertEquals(options.toJson(), copy.toJson()); @@ -746,6 +794,9 @@ public void testServerOptionsJson() { long sslHandshakeTimeout = TestUtils.randomPositiveLong(); boolean useProxyProtocol = TestUtils.randomBoolean(); long proxyProtocolTimeout = TestUtils.randomPositiveLong(); + int tcpKeepAliveIdleSeconds = TestUtils.randomPositiveInt(); + int tcpKeepAliveCount = TestUtils.randomPositiveInt(); + int tcpKeepAliveIntervalSeconds = TestUtils.randomPositiveInt(); JsonObject json = new JsonObject(); json.put("sendBufferSize", sendBufferSize) @@ -772,7 +823,10 @@ public void testServerOptionsJson() { .put("sni", sni) .put("sslHandshakeTimeout", sslHandshakeTimeout) .put("useProxyProtocol", useProxyProtocol) - .put("proxyProtocolTimeout", proxyProtocolTimeout); + .put("proxyProtocolTimeout", proxyProtocolTimeout) + .put("tcpKeepAliveIdleSeconds", tcpKeepAliveIdleSeconds) + .put("tcpKeepAliveCount", tcpKeepAliveCount) + .put("tcpKeepAliveIntervalSeconds", tcpKeepAliveIntervalSeconds); NetServerOptions options = new NetServerOptions(json); assertEquals(sendBufferSize, options.getSendBufferSize()); @@ -814,6 +868,9 @@ public void testServerOptionsJson() { assertEquals(sni, options.isSni()); assertEquals(useProxyProtocol, options.isUseProxyProtocol()); assertEquals(proxyProtocolTimeout, options.getProxyProtocolTimeout()); + assertEquals(tcpKeepAliveIdleSeconds, options.getTcpKeepAliveIdleSeconds()); + assertEquals(tcpKeepAliveCount, options.getTcpKeepAliveCount()); + assertEquals(tcpKeepAliveIntervalSeconds, options.getTcpKeepAliveIntervalSeconds()); // Test other keystore/truststore types json.remove("keyStoreOptions");