Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature 5163 configurable socket keepalive interval #5183

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,21 @@ static void fromJson(Iterable<java.util.Map.Entry<String, Object>> json, EventBu
obj.setTcpKeepAlive((Boolean)member.getValue());
}
break;
case "tcpKeepAliveCount":
if (member.getValue() instanceof Number) {
obj.setTcpKeepAliveCount(((Number)member.getValue()).intValue());
}
break;
case "tcpKeepAliveIdleSeconds":
if (member.getValue() instanceof Number) {
obj.setTcpKeepAliveIdleSeconds(((Number)member.getValue()).intValue());
}
break;
case "tcpKeepAliveIntervalSeconds":
if (member.getValue() instanceof Number) {
obj.setTcpKeepAliveIntervalSeconds(((Number)member.getValue()).intValue());
}
break;
case "tcpNoDelay":
if (member.getValue() instanceof Boolean) {
obj.setTcpNoDelay((Boolean)member.getValue());
Expand Down Expand Up @@ -361,6 +376,9 @@ static void toJson(EventBusOptions obj, java.util.Map<String, Object> json) {
json.put("tcpCork", obj.isTcpCork());
json.put("tcpFastOpen", obj.isTcpFastOpen());
json.put("tcpKeepAlive", obj.isTcpKeepAlive());
json.put("tcpKeepAliveCount", obj.getTcpKeepAliveCount());
json.put("tcpKeepAliveIdleSeconds", obj.getTcpKeepAliveIdleSeconds());
json.put("tcpKeepAliveIntervalSeconds", obj.getTcpKeepAliveIntervalSeconds());
json.put("tcpNoDelay", obj.isTcpNoDelay());
json.put("tcpQuickAck", obj.isTcpQuickAck());
json.put("tcpUserTimeout", obj.getTcpUserTimeout());
Expand Down
18 changes: 18 additions & 0 deletions src/main/generated/io/vertx/core/net/TCPSSLOptionsConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,21 @@ static void fromJson(Iterable<java.util.Map.Entry<String, Object>> json, TCPSSLO
obj.setTcpKeepAlive((Boolean)member.getValue());
}
break;
case "tcpKeepAliveCount":
if (member.getValue() instanceof Number) {
obj.setTcpKeepAliveCount(((Number)member.getValue()).intValue());
}
break;
case "tcpKeepAliveIdleSeconds":
if (member.getValue() instanceof Number) {
obj.setTcpKeepAliveIdleSeconds(((Number)member.getValue()).intValue());
}
break;
case "tcpKeepAliveIntervalSeconds":
if (member.getValue() instanceof Number) {
obj.setTcpKeepAliveIntervalSeconds(((Number)member.getValue()).intValue());
}
break;
case "tcpNoDelay":
if (member.getValue() instanceof Boolean) {
obj.setTcpNoDelay((Boolean)member.getValue());
Expand Down Expand Up @@ -233,6 +248,9 @@ static void toJson(TCPSSLOptions obj, java.util.Map<String, Object> json) {
json.put("tcpCork", obj.isTcpCork());
json.put("tcpFastOpen", obj.isTcpFastOpen());
json.put("tcpKeepAlive", obj.isTcpKeepAlive());
json.put("tcpKeepAliveCount", obj.getTcpKeepAliveCount());
json.put("tcpKeepAliveIdleSeconds", obj.getTcpKeepAliveIdleSeconds());
json.put("tcpKeepAliveIntervalSeconds", obj.getTcpKeepAliveIntervalSeconds());
json.put("tcpNoDelay", obj.isTcpNoDelay());
json.put("tcpQuickAck", obj.isTcpQuickAck());
json.put("tcpUserTimeout", obj.getTcpUserTimeout());
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/io/vertx/core/impl/transports/EpollTransport.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,16 @@ public void configure(NetServerOptions options, boolean domainSocket, ServerBoot
}
bootstrap.childOption(EpollChannelOption.TCP_QUICKACK, options.isTcpQuickAck());
bootstrap.childOption(EpollChannelOption.TCP_CORK, options.isTcpCork());

if (options.isTcpKeepAlive() && options.getTcpKeepAliveIdleSeconds() != -1) {
bootstrap.childOption(EpollChannelOption.TCP_KEEPIDLE, options.getTcpKeepAliveIdleSeconds());
}
if (options.isTcpKeepAlive() && options.getTcpKeepAliveCount() != -1) {
bootstrap.childOption(EpollChannelOption.TCP_KEEPCNT, options.getTcpKeepAliveCount());
}
if (options.isTcpKeepAlive() && options.getTcpKeepAliveIntervalSeconds() != -1) {
bootstrap.childOption(EpollChannelOption.TCP_KEEPINTVL, options.getTcpKeepAliveIntervalSeconds());
}
}
Transport.super.configure(options, domainSocket, bootstrap);
}
Expand Down
81 changes: 81 additions & 0 deletions src/main/java/io/vertx/core/net/TCPSSLOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -40,6 +41,21 @@ public abstract class TCPSSLOptions extends NetworkOptions {
*/
public static final boolean DEFAULT_TCP_KEEP_ALIVE = false;

/**
* Default value for tcp keepalive idle time -1 defaults to OS settings
*/
public static final int DEFAULT_TCP_KEEPALIVE_IDLE_SECONDS = -1;

/**
* Default value for tcp keepalive count -1 defaults to OS settings
*/
public static final int DEFAULT_TCP_KEEPALIVE_COUNT = -1;

/**
* Default value for tcp keepalive interval -1 defaults to OS settings
*/
public static final int DEFAULT_TCP_KEEAPLIVE_INTERVAL_SECONDS = -1;

/**
* The default value of SO_linger = -1
*/
Expand Down Expand Up @@ -119,6 +135,9 @@ public abstract class TCPSSLOptions extends NetworkOptions {

private boolean tcpNoDelay;
private boolean tcpKeepAlive;
private int tcpKeepAliveIdleSeconds;
private int tcpKeepAliveCount;
private int tcpKeepAliveIntervalSeconds;
private int soLinger;
private int idleTimeout;
private int readIdleTimeout;
Expand Down Expand Up @@ -149,6 +168,9 @@ public TCPSSLOptions(TCPSSLOptions other) {
super(other);
this.tcpNoDelay = other.isTcpNoDelay();
this.tcpKeepAlive = other.isTcpKeepAlive();
this.tcpKeepAliveIdleSeconds = other.getTcpKeepAliveIdleSeconds();
this.tcpKeepAliveCount = other.getTcpKeepAliveCount();
this.tcpKeepAliveIntervalSeconds = other.getTcpKeepAliveIntervalSeconds();
this.soLinger = other.getSoLinger();
this.idleTimeout = other.getIdleTimeout();
this.idleTimeoutUnit = other.getIdleTimeoutUnit() != null ? other.getIdleTimeoutUnit() : DEFAULT_IDLE_TIMEOUT_TIME_UNIT;
Expand Down Expand Up @@ -188,6 +210,9 @@ public JsonObject toJson() {
private void init() {
tcpNoDelay = DEFAULT_TCP_NO_DELAY;
tcpKeepAlive = DEFAULT_TCP_KEEP_ALIVE;
tcpKeepAliveIdleSeconds = DEFAULT_TCP_KEEPALIVE_IDLE_SECONDS;
tcpKeepAliveCount = DEFAULT_TCP_KEEPALIVE_COUNT;
tcpKeepAliveIntervalSeconds = DEFAULT_TCP_KEEAPLIVE_INTERVAL_SECONDS;
soLinger = DEFAULT_SO_LINGER;
idleTimeout = DEFAULT_IDLE_TIMEOUT;
readIdleTimeout = DEFAULT_READ_IDLE_TIMEOUT;
Expand Down Expand Up @@ -243,6 +268,62 @@ public TCPSSLOptions setTcpKeepAlive(boolean tcpKeepAlive) {
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;
}

/**
*
* @return is SO_linger enabled
Expand Down
38 changes: 37 additions & 1 deletion src/test/java/io/vertx/core/net/NetTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,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();
}

Expand Down Expand Up @@ -638,6 +659,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);
Expand All @@ -662,6 +686,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());
Expand Down Expand Up @@ -731,6 +758,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)
Expand All @@ -756,7 +786,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());
Expand Down Expand Up @@ -797,6 +830,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");
Expand Down
Loading