Skip to content
Closed
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 @@ -66,6 +66,7 @@
* @author HaiTao Zhang
* @author Victor Mandujano
* @author Chris Bono
* @author Michael Lubavin
* @since 1.0.0
*/
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
Expand Down Expand Up @@ -395,6 +396,19 @@ public static class Tomcat {
*/
private Duration connectionTimeout;

/**
* Amount of time a connection is allowed to remain idle until it is closed. Set
* to -1 to indicate idle connections should not be closed.
*/
private Duration keepAliveTimeout;

/**
* The maximum number of requests which can be processed on one connection before
* it is closed by the server. Set to -1 for an unlimited number of requests per
* connection. The default is 100, same as Tomcat default.
*/
private int maxKeepAliveRequests = 100;

/**
* Static resource configuration.
*/
Expand Down Expand Up @@ -530,6 +544,22 @@ public void setConnectionTimeout(Duration connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}

public Duration getKeepAliveTimeout() {
return this.keepAliveTimeout;
}

public void setKeepAliveTimeout(Duration keepAliveTimeout) {
this.keepAliveTimeout = keepAliveTimeout;
}

public int getMaxKeepAliveRequests() {
return this.maxKeepAliveRequests;
}

public void setMaxKeepAliveRequests(int maxKeepAliveRequests) {
this.maxKeepAliveRequests = maxKeepAliveRequests;
}

public Resource getResource() {
return this.resource;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ public void customize(ConfigurableTomcatWebServerFactory factory) {
propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull().to(factory::setUriEncoding);
propertyMapper.from(tomcatProperties::getConnectionTimeout).whenNonNull()
.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
propertyMapper.from(tomcatProperties::getKeepAliveTimeout).whenNonNull()
.to((keepAliveTimeout) -> customizeKeepAliveTimeout(factory, keepAliveTimeout));
propertyMapper.from(tomcatProperties::getMaxKeepAliveRequests)
.to((maxKeepAliveRequests) -> customizeMaxKeepAliveRequests(factory, maxKeepAliveRequests));
propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive)
.to((maxConnections) -> customizeMaxConnections(factory, maxConnections));
propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive)
Expand Down Expand Up @@ -159,6 +163,26 @@ private void customizeConnectionTimeout(ConfigurableTomcatWebServerFactory facto
});
}

private void customizeKeepAliveTimeout(ConfigurableTomcatWebServerFactory factory, Duration keepAliveTimeout) {
factory.addConnectorCustomizers((connector) -> {
ProtocolHandler handler = connector.getProtocolHandler();
if (handler instanceof AbstractProtocol) {
AbstractProtocol<?> protocol = (AbstractProtocol<?>) handler;
protocol.setKeepAliveTimeout((int) keepAliveTimeout.toMillis());
}
});
}

private void customizeMaxKeepAliveRequests(ConfigurableTomcatWebServerFactory factory, int maxKeepAliveRequests) {
factory.addConnectorCustomizers((connector) -> {
ProtocolHandler handler = connector.getProtocolHandler();
if (handler instanceof AbstractHttp11Protocol) {
AbstractHttp11Protocol<?> protocol = (AbstractHttp11Protocol<?>) handler;
protocol.setMaxKeepAliveRequests(maxKeepAliveRequests);
}
});
}

private void customizeRelaxedPathChars(ConfigurableTomcatWebServerFactory factory, String relaxedChars) {
factory.addConnectorCustomizers((connector) -> connector.setProperty("relaxedPathChars", relaxedChars));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
import org.apache.catalina.valves.AccessLogValve;
import org.apache.catalina.valves.RemoteIpValve;
import org.apache.coyote.AbstractProtocol;
import org.apache.coyote.http11.Http11NioProtocol;
import org.apache.tomcat.util.net.NioEndpoint;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
Expand Down Expand Up @@ -82,6 +84,7 @@
* @author HaiTao Zhang
* @author Rafiullah Hamedy
* @author Chris Bono
* @author Michael Lubavin
*/
class ServerPropertiesTests {

Expand Down Expand Up @@ -132,6 +135,8 @@ void testTomcatBinding() {
map.put("server.tomcat.relaxed-path-chars", "|,<");
map.put("server.tomcat.relaxed-query-chars", "^ , | ");
map.put("server.tomcat.use-relative-redirects", "true");
map.put("server.tomcat.keep-alive-timeout", "100s");
map.put("server.tomcat.max-keep-alive-requests", "-1");
bind(map);
ServerProperties.Tomcat tomcat = this.properties.getTomcat();
Accesslog accesslog = tomcat.getAccesslog();
Expand All @@ -154,6 +159,8 @@ void testTomcatBinding() {
assertThat(tomcat.getRelaxedPathChars()).containsExactly('|', '<');
assertThat(tomcat.getRelaxedQueryChars()).containsExactly('^', '|');
assertThat(tomcat.isUseRelativeRedirects()).isTrue();
assertThat(tomcat.getKeepAliveTimeout()).hasSeconds(100);
assertThat(tomcat.getMaxKeepAliveRequests()).isEqualTo(-1);
}

@Test
Expand Down Expand Up @@ -325,6 +332,16 @@ void tomcatMaxThreadsMatchesProtocolDefault() throws Exception {
assertThat(this.properties.getTomcat().getThreads().getMax()).isEqualTo(getDefaultProtocol().getMaxThreads());
}

@Test
void tomcatMaxKeepAliveRequestsMatchesProtocolDefault() throws Exception {
Http11NioProtocol http11NioProtocol = (Http11NioProtocol) getDefaultProtocol();
NioEndpoint endpoint = (NioEndpoint) ReflectionTestUtils.getField(http11NioProtocol, "endpoint");
// cannot use getter since it returns 1 when endpoint is not bound,
// so using reflection to fetch the default maxKeepAliveRequests instead
int defaultMaxKeepAliveRequests = (int) ReflectionTestUtils.getField(endpoint, "maxKeepAliveRequests");
assertThat(this.properties.getTomcat().getMaxKeepAliveRequests()).isEqualTo(defaultMaxKeepAliveRequests);
}

@Test
void tomcatMinSpareThreadsMatchesProtocolDefault() throws Exception {
assertThat(this.properties.getTomcat().getThreads().getMinSpare())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,29 @@ void customConnectionTimeout() {
.isEqualTo(30000));
}

@Test
void customKeepAliveTimeout() {
bind("server.tomcat.keep-alive-timeout=50s");
customizeAndRunServer((server) -> assertThat(
((AbstractProtocol<?>) server.getTomcat().getConnector().getProtocolHandler()).getKeepAliveTimeout())
.isEqualTo(50000));
}

@Test
void defaultMaxKeepAliveRequests() {
customizeAndRunServer((server) -> assertThat(
((AbstractHttp11Protocol<?>) server.getTomcat().getConnector().getProtocolHandler())
.getMaxKeepAliveRequests()).isEqualTo(100));
}

@Test
void customMaxKeepAliveRequests() {
bind("server.tomcat.max-keep-alive-requests=-1");
customizeAndRunServer((server) -> assertThat(
((AbstractHttp11Protocol<?>) server.getTomcat().getConnector().getProtocolHandler())
.getMaxKeepAliveRequests()).isEqualTo(-1));
}

@Test
void accessLogBufferingCanBeDisabled() {
bind("server.tomcat.accesslog.enabled=true", "server.tomcat.accesslog.buffered=false");
Expand Down