Merge pull request #25815 from parviz-93

* gh-25815:
  Polish "Add config props for keep-alive timeout and max keep-alive reqs"
  Add config props for keep-alive timeout and max keep-alive reqs

Closes gh-25815
pull/25943/head
Andy Wilkinson 4 years ago
commit e7314623cb

@ -66,6 +66,7 @@ import org.springframework.util.unit.DataSize;
* @author HaiTao Zhang * @author HaiTao Zhang
* @author Victor Mandujano * @author Victor Mandujano
* @author Chris Bono * @author Chris Bono
* @author Parviz Rozikov
* @since 1.0.0 * @since 1.0.0
*/ */
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
@ -370,6 +371,19 @@ public class ServerProperties {
*/ */
private int processorCache = 200; private int processorCache = 200;
/**
* Time to wait for another HTTP request before the connection is closed. When not
* set the connectionTimeout is used. When set to -1 there will be no timeout.
*/
private Duration keepAliveTimeout;
/**
* Maximum number of HTTP requests that can be pipelined before the connection is
* closed. When set to 0 or 1, keep-alive and pipelining are disabled. When set to
* -1, an unlimited number of pipelined or keep-alive requests is allowed.
*/
private int maxKeepAliveRequests = 100;
/** /**
* Comma-separated list of additional patterns that match jars to ignore for TLD * Comma-separated list of additional patterns that match jars to ignore for TLD
* scanning. The special '?' and '*' characters can be used in the pattern to * scanning. The special '?' and '*' characters can be used in the pattern to
@ -498,6 +512,22 @@ public class ServerProperties {
this.processorCache = processorCache; this.processorCache = processorCache;
} }
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 List<String> getAdditionalTldSkipPatterns() { public List<String> getAdditionalTldSkipPatterns() {
return this.additionalTldSkipPatterns; return this.additionalTldSkipPatterns;
} }

@ -56,6 +56,7 @@ import org.springframework.util.unit.DataSize;
* @author Dirk Deyne * @author Dirk Deyne
* @author Rafiullah Hamedy * @author Rafiullah Hamedy
* @author Victor Mandujano * @author Victor Mandujano
* @author Parviz Rozikov
* @since 2.0.0 * @since 2.0.0
*/ */
public class TomcatWebServerFactoryCustomizer public class TomcatWebServerFactoryCustomizer
@ -108,6 +109,10 @@ public class TomcatWebServerFactoryCustomizer
.to((acceptCount) -> customizeAcceptCount(factory, acceptCount)); .to((acceptCount) -> customizeAcceptCount(factory, acceptCount));
propertyMapper.from(tomcatProperties::getProcessorCache) propertyMapper.from(tomcatProperties::getProcessorCache)
.to((processorCache) -> customizeProcessorCache(factory, processorCache)); .to((processorCache) -> customizeProcessorCache(factory, processorCache));
propertyMapper.from(tomcatProperties::getKeepAliveTimeout).whenNonNull()
.to((keepAliveTimeout) -> customizeKeepAliveTimeout(factory, keepAliveTimeout));
propertyMapper.from(tomcatProperties::getMaxKeepAliveRequests)
.to((maxKeepAliveRequests) -> customizeMaxKeepAliveRequests(factory, maxKeepAliveRequests));
propertyMapper.from(tomcatProperties::getRelaxedPathChars).as(this::joinCharacters).whenHasText() propertyMapper.from(tomcatProperties::getRelaxedPathChars).as(this::joinCharacters).whenHasText()
.to((relaxedChars) -> customizeRelaxedPathChars(factory, relaxedChars)); .to((relaxedChars) -> customizeRelaxedPathChars(factory, relaxedChars));
propertyMapper.from(tomcatProperties::getRelaxedQueryChars).as(this::joinCharacters).whenHasText() propertyMapper.from(tomcatProperties::getRelaxedQueryChars).as(this::joinCharacters).whenHasText()
@ -139,6 +144,26 @@ public class TomcatWebServerFactoryCustomizer
}); });
} }
private void customizeKeepAliveTimeout(ConfigurableTomcatWebServerFactory factory, Duration keepAliveTimeout) {
factory.addConnectorCustomizers((connector) -> {
ProtocolHandler handler = connector.getProtocolHandler();
if (handler instanceof AbstractProtocol) {
final 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 customizeMaxConnections(ConfigurableTomcatWebServerFactory factory, int maxConnections) { private void customizeMaxConnections(ConfigurableTomcatWebServerFactory factory, int maxConnections) {
factory.addConnectorCustomizers((connector) -> { factory.addConnectorCustomizers((connector) -> {
ProtocolHandler handler = connector.getProtocolHandler(); ProtocolHandler handler = connector.getProtocolHandler();

@ -38,6 +38,7 @@ import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.valves.AccessLogValve; import org.apache.catalina.valves.AccessLogValve;
import org.apache.catalina.valves.RemoteIpValve; import org.apache.catalina.valves.RemoteIpValve;
import org.apache.coyote.AbstractProtocol; import org.apache.coyote.AbstractProtocol;
import org.apache.tomcat.util.net.AbstractEndpoint;
import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
@ -82,6 +83,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author HaiTao Zhang * @author HaiTao Zhang
* @author Rafiullah Hamedy * @author Rafiullah Hamedy
* @author Chris Bono * @author Chris Bono
* @author Parviz Rozikov
*/ */
class ServerPropertiesTests { class ServerPropertiesTests {
@ -216,6 +218,31 @@ class ServerPropertiesTests {
assertThat(this.properties.getTomcat().getThreads().getMax()).isEqualTo(10); assertThat(this.properties.getTomcat().getThreads().getMax()).isEqualTo(10);
} }
@Test
void testCustomizeTomcatKeepAliveTimeout() {
bind("server.tomcat.keep-alive-timeout", "30s");
assertThat(this.properties.getTomcat().getKeepAliveTimeout()).hasSeconds(30);
}
@Test
void testCustomizeTomcatKeepAliveTimeoutWithInfinite() {
bind("server.tomcat.keep-alive-timeout", "-1");
assertThat(this.properties.getTomcat().getKeepAliveTimeout().toMillis()).isEqualTo(-1);
assertThat(this.properties.getTomcat().getKeepAliveTimeout()).hasMillis(-1);
}
@Test
void customizeMaxKeepAliveRequests() {
bind("server.tomcat.max-keep-alive-requests", "200");
assertThat(this.properties.getTomcat().getMaxKeepAliveRequests()).isEqualTo(200);
}
@Test
void customizeMaxKeepAliveRequestsWithInfinite() {
bind("server.tomcat.max-keep-alive-requests", "-1");
assertThat(this.properties.getTomcat().getMaxKeepAliveRequests()).isEqualTo(-1);
}
@Test @Test
void testCustomizeTomcatMinSpareThreads() { void testCustomizeTomcatMinSpareThreads() {
bind("server.tomcat.threads.min-spare", "10"); bind("server.tomcat.threads.min-spare", "10");
@ -384,6 +411,14 @@ class ServerPropertiesTests {
assertThat(this.properties.getTomcat().isUseRelativeRedirects()).isFalse(); assertThat(this.properties.getTomcat().isUseRelativeRedirects()).isFalse();
} }
@Test
void tomcatMaxKeepAliveRequestsDefault() throws Exception {
AbstractEndpoint<?, ?> endpoint = (AbstractEndpoint<?, ?>) ReflectionTestUtils.getField(getDefaultProtocol(),
"endpoint");
int defaultMaxKeepAliveRequests = (int) ReflectionTestUtils.getField(endpoint, "maxKeepAliveRequests");
assertThat(this.properties.getTomcat().getMaxKeepAliveRequests()).isEqualTo(defaultMaxKeepAliveRequests);
}
@Test @Test
void jettyThreadPoolPropertyDefaultsShouldMatchServerDefault() { void jettyThreadPoolPropertyDefaultsShouldMatchServerDefault() {
JettyServletWebServerFactory jettyFactory = new JettyServletWebServerFactory(0); JettyServletWebServerFactory jettyFactory = new JettyServletWebServerFactory(0);

@ -56,6 +56,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Andrew McGhie * @author Andrew McGhie
* @author Rafiullah Hamedy * @author Rafiullah Hamedy
* @author Victor Mandujano * @author Victor Mandujano
* @author Parviz Rozikov
*/ */
class TomcatWebServerFactoryCustomizerTests { class TomcatWebServerFactoryCustomizerTests {
@ -97,6 +98,29 @@ class TomcatWebServerFactoryCustomizerTests {
.isEqualTo(100)); .isEqualTo(100));
} }
@Test
void customKeepAliveTimeout() {
bind("server.tomcat.keep-alive-timeout=30ms");
customizeAndRunServer((server) -> assertThat(
((AbstractProtocol<?>) server.getTomcat().getConnector().getProtocolHandler()).getKeepAliveTimeout())
.isEqualTo(30));
}
@Test
void customMaxKeepAliveRequests() {
bind("server.tomcat.max-keep-alive-requests=-1");
customizeAndRunServer((server) -> assertThat(
((AbstractHttp11Protocol<?>) server.getTomcat().getConnector().getProtocolHandler())
.getMaxKeepAliveRequests()).isEqualTo(-1));
}
@Test
void defaultMaxKeepAliveRequests() {
customizeAndRunServer((server) -> assertThat(
((AbstractHttp11Protocol<?>) server.getTomcat().getConnector().getProtocolHandler())
.getMaxKeepAliveRequests()).isEqualTo(100));
}
@Test @Test
void unlimitedProcessorCache() { void unlimitedProcessorCache() {
bind("server.tomcat.processor-cache=-1"); bind("server.tomcat.processor-cache=-1");

Loading…
Cancel
Save