Skip to content

Commit e4fa9ce

Browse files
committed
Deprecate server.connection-timeout property
Prior to this commit, all supported servers would share the same configuration property `server.connection-timeout`. Unfortunately, in many cases the behavior of this timeout changes depending on the server. From actual connection setup timeout, to detecting and closing idle connections, this property cannot be properly translated from one server implementation to another. This commit deprecates this configuration property and introduces server specific properties: * `server.jetty.connection-idle-timeout` (Time that the connection can be idle before it is closed.) * `server.netty.connection-timeout` (Connection timeout of the Netty channel.) * `server.tomcat.connection-timeout` (Amount of time the connector will wait, after accepting a connection, for the request URI line to be presented.) * `server.undertow.no-request-timeout` (Amount of time a connection can sit idle without processing a request, before it is closed by the server.) `server.connection-timeout` is now deprecated and will be removed in a future release. Fixes gh-18473
1 parent 4eaa387 commit e4fa9ce

File tree

10 files changed

+143
-12
lines changed

10 files changed

+143
-12
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ public class ServerProperties {
111111

112112
private final Jetty jetty = new Jetty();
113113

114+
private final Netty netty = new Netty();
115+
114116
private final Undertow undertow = new Undertow();
115117

116118
public Integer getPort() {
@@ -153,10 +155,14 @@ public void setMaxHttpHeaderSize(DataSize maxHttpHeaderSize) {
153155
this.maxHttpHeaderSize = maxHttpHeaderSize;
154156
}
155157

158+
@Deprecated
159+
@DeprecatedConfigurationProperty(
160+
reason = "Each server behaves differently. Use server specific properties instead.")
156161
public Duration getConnectionTimeout() {
157162
return this.connectionTimeout;
158163
}
159164

165+
@Deprecated
160166
public void setConnectionTimeout(Duration connectionTimeout) {
161167
this.connectionTimeout = connectionTimeout;
162168
}
@@ -193,6 +199,10 @@ public Jetty getJetty() {
193199
return this.jetty;
194200
}
195201

202+
public Netty getNetty() {
203+
return this.netty;
204+
}
205+
196206
public Undertow getUndertow() {
197207
return this.undertow;
198208
}
@@ -376,6 +386,12 @@ public static class Tomcat {
376386
*/
377387
private List<String> additionalTldSkipPatterns = new ArrayList<>();
378388

389+
/**
390+
* Amount of time the connector will wait, after accepting a connection, for the
391+
* request URI line to be presented.
392+
*/
393+
private Duration connectionTimeout;
394+
379395
/**
380396
* Static resource configuration.
381397
*/
@@ -532,6 +548,14 @@ public void setAdditionalTldSkipPatterns(List<String> additionalTldSkipPatterns)
532548
this.additionalTldSkipPatterns = additionalTldSkipPatterns;
533549
}
534550

551+
public Duration getConnectionTimeout() {
552+
return this.connectionTimeout;
553+
}
554+
555+
public void setConnectionTimeout(Duration connectionTimeout) {
556+
this.connectionTimeout = connectionTimeout;
557+
}
558+
535559
public Resource getResource() {
536560
return this.resource;
537561
}
@@ -738,6 +762,11 @@ public static class Jetty {
738762
*/
739763
private Integer selectors = -1;
740764

765+
/**
766+
* Time that the connection can be idle before it is closed.
767+
*/
768+
private Duration connectionIdleTimeout;
769+
741770
public Accesslog getAccesslog() {
742771
return this.accesslog;
743772
}
@@ -766,6 +795,14 @@ public void setSelectors(Integer selectors) {
766795
this.selectors = selectors;
767796
}
768797

798+
public Duration getConnectionIdleTimeout() {
799+
return this.connectionIdleTimeout;
800+
}
801+
802+
public void setConnectionIdleTimeout(Duration connectionIdleTimeout) {
803+
this.connectionIdleTimeout = connectionIdleTimeout;
804+
}
805+
769806
/**
770807
* Jetty access log properties.
771808
*/
@@ -931,6 +968,26 @@ public void setLogLatency(boolean logLatency) {
931968

932969
}
933970

971+
/**
972+
* Netty properties.
973+
*/
974+
public static class Netty {
975+
976+
/**
977+
* Connection timeout of the Netty channel.
978+
*/
979+
private Duration connectionTimeout;
980+
981+
public Duration getConnectionTimeout() {
982+
return this.connectionTimeout;
983+
}
984+
985+
public void setConnectionTimeout(Duration connectionTimeout) {
986+
this.connectionTimeout = connectionTimeout;
987+
}
988+
989+
}
990+
934991
/**
935992
* Undertow properties.
936993
*/
@@ -970,6 +1027,12 @@ public static class Undertow {
9701027
*/
9711028
private boolean eagerFilterInit = true;
9721029

1030+
/**
1031+
* Amount of time a connection can sit idle without processing a request, before
1032+
* it is closed by the server.
1033+
*/
1034+
private Duration noRequestTimeout;
1035+
9731036
private final Accesslog accesslog = new Accesslog();
9741037

9751038
public DataSize getMaxHttpPostSize() {
@@ -1020,6 +1083,14 @@ public void setEagerFilterInit(boolean eagerFilterInit) {
10201083
this.eagerFilterInit = eagerFilterInit;
10211084
}
10221085

1086+
public Duration getNoRequestTimeout() {
1087+
return this.noRequestTimeout;
1088+
}
1089+
1090+
public void setNoRequestTimeout(Duration noRequestTimeout) {
1091+
this.noRequestTimeout = noRequestTimeout;
1092+
}
1093+
10231094
public Accesslog getAccesslog() {
10241095
return this.accesslog;
10251096
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ public void customize(ConfigurableJettyWebServerFactory factory) {
7777
propertyMapper.from(jettyProperties::getMaxHttpPostSize).asInt(DataSize::toBytes).when(this::isPositive)
7878
.to((maxHttpPostSize) -> customizeMaxHttpPostSize(factory, maxHttpPostSize));
7979
propertyMapper.from(properties::getConnectionTimeout).whenNonNull()
80-
.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
80+
.to((connectionTimeout) -> customizeIdleTimeout(factory, connectionTimeout));
81+
propertyMapper.from(jettyProperties::getConnectionIdleTimeout).whenNonNull()
82+
.to((idleTimeout) -> customizeIdleTimeout(factory, idleTimeout));
8183
propertyMapper.from(jettyProperties::getAccesslog).when(ServerProperties.Jetty.Accesslog::isEnabled)
8284
.to((accesslog) -> customizeAccessLog(factory, accesslog));
8385
}
@@ -94,7 +96,7 @@ private boolean getOrDeduceUseForwardHeaders(ServerProperties serverProperties,
9496
return platform != null && platform.isUsingForwardHeaders();
9597
}
9698

97-
private void customizeConnectionTimeout(ConfigurableJettyWebServerFactory factory, Duration connectionTimeout) {
99+
private void customizeIdleTimeout(ConfigurableJettyWebServerFactory factory, Duration connectionTimeout) {
98100
factory.addServerCustomizers((server) -> {
99101
for (org.eclipse.jetty.server.Connector connector : server.getConnectors()) {
100102
if (connector instanceof AbstractConnector) {

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/NettyWebServerFactoryCustomizer.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ public void customize(NettyReactiveWebServerFactory factory) {
6161
propertyMapper.from(this.serverProperties::getMaxHttpHeaderSize)
6262
.to((maxHttpRequestHeaderSize) -> customizeMaxHttpHeaderSize(factory, maxHttpRequestHeaderSize));
6363
propertyMapper.from(this.serverProperties::getConnectionTimeout)
64+
.to((connectionTimeout) -> customizeGenericConnectionTimeout(factory, connectionTimeout));
65+
ServerProperties.Netty nettyProperties = this.serverProperties.getNetty();
66+
propertyMapper.from(nettyProperties::getConnectionTimeout).whenNonNull()
6467
.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
6568
}
6669

@@ -77,12 +80,17 @@ private void customizeMaxHttpHeaderSize(NettyReactiveWebServerFactory factory, D
7780
(httpRequestDecoderSpec) -> httpRequestDecoderSpec.maxHeaderSize((int) maxHttpHeaderSize.toBytes())));
7881
}
7982

80-
private void customizeConnectionTimeout(NettyReactiveWebServerFactory factory, Duration connectionTimeout) {
83+
private void customizeGenericConnectionTimeout(NettyReactiveWebServerFactory factory, Duration connectionTimeout) {
8184
if (!connectionTimeout.isZero()) {
8285
long timeoutMillis = connectionTimeout.isNegative() ? 0 : connectionTimeout.toMillis();
8386
factory.addServerCustomizers((httpServer) -> httpServer.tcpConfiguration((tcpServer) -> tcpServer
8487
.selectorOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, (int) timeoutMillis)));
8588
}
8689
}
8790

91+
private void customizeConnectionTimeout(NettyReactiveWebServerFactory factory, Duration connectionTimeout) {
92+
factory.addServerCustomizers((httpServer) -> httpServer.tcpConfiguration((tcpServer) -> tcpServer
93+
.selectorOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, (int) connectionTimeout.toMillis())));
94+
}
95+
8896
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ public void customize(ConfigurableTomcatWebServerFactory factory) {
9494
propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull().to(factory::setUriEncoding);
9595
propertyMapper.from(properties::getConnectionTimeout).whenNonNull()
9696
.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
97+
propertyMapper.from(tomcatProperties::getConnectionTimeout).whenNonNull()
98+
.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
9799
propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive)
98100
.to((maxConnections) -> customizeMaxConnections(factory, maxConnections));
99101
propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive)

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,16 @@ public void customize(ConfigurableUndertowWebServerFactory factory) {
8181
propertyMapper.from(undertowProperties::getMaxHttpPostSize).asInt(DataSize::toBytes).when(this::isPositive)
8282
.to((maxHttpPostSize) -> customizeMaxHttpPostSize(factory, maxHttpPostSize));
8383
propertyMapper.from(properties::getConnectionTimeout)
84-
.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
84+
.to((connectionTimeout) -> customizeNoRequestTimeout(factory, connectionTimeout));
85+
propertyMapper.from(undertowProperties::getNoRequestTimeout).whenNonNull()
86+
.to((connectionTimeout) -> customizeNoRequestTimeout(factory, connectionTimeout));
8587
}
8688

8789
private boolean isPositive(Number value) {
8890
return value.longValue() > 0;
8991
}
9092

91-
private void customizeConnectionTimeout(ConfigurableUndertowWebServerFactory factory, Duration connectionTimeout) {
93+
private void customizeNoRequestTimeout(ConfigurableUndertowWebServerFactory factory, Duration connectionTimeout) {
9294
factory.addBuilderCustomizers((builder) -> builder.setServerOption(UndertowOptions.NO_REQUEST_TIMEOUT,
9395
(int) connectionTimeout.toMillis()));
9496
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@
1919
import java.io.File;
2020
import java.io.IOException;
2121
import java.util.ArrayList;
22+
import java.util.Arrays;
2223
import java.util.List;
2324
import java.util.Locale;
2425
import java.util.TimeZone;
26+
import java.util.stream.Collectors;
2527

28+
import org.eclipse.jetty.server.AbstractConnector;
2629
import org.eclipse.jetty.server.Connector;
2730
import org.eclipse.jetty.server.HttpConfiguration;
2831
import org.eclipse.jetty.server.HttpConfiguration.ConnectionFactory;
@@ -159,6 +162,23 @@ public void customMaxHttpHeaderSizeIgnoredIfZero() {
159162
assertThat(requestHeaderSizes).containsOnly(8192);
160163
}
161164

165+
@Test
166+
public void customIdleTimeout() {
167+
bind("server.jetty.idle-timeout=60s");
168+
JettyWebServer server = customizeAndGetServer();
169+
List<Long> timeouts = connectorsIdleTimeouts(server);
170+
assertThat(timeouts).containsOnly(60000L);
171+
}
172+
173+
private List<Long> connectorsIdleTimeouts(JettyWebServer server) {
174+
// Start (and directly stop) server to have connectors available
175+
server.start();
176+
server.stop();
177+
return Arrays.stream(server.getServer().getConnectors())
178+
.filter((connector) -> connector instanceof AbstractConnector).map(Connector::getIdleTimeout)
179+
.collect(Collectors.toList());
180+
}
181+
162182
private List<Integer> getRequestHeaderSizes(JettyWebServer server) {
163183
List<Integer> requestHeaderSizes = new ArrayList<>();
164184
// Start (and directly stop) server to have connectors available

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/NettyWebServerFactoryCustomizerTests.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,21 +93,29 @@ public void setUseForwardHeaders() {
9393
}
9494

9595
@Test
96-
public void setConnectionTimeoutAsZero() {
97-
setupConnectionTimeout(Duration.ZERO);
96+
public void setServerConnectionTimeoutAsZero() {
97+
setupServerConnectionTimeout(Duration.ZERO);
9898
NettyReactiveWebServerFactory factory = mock(NettyReactiveWebServerFactory.class);
9999
this.customizer.customize(factory);
100100
verifyConnectionTimeout(factory, null);
101101
}
102102

103103
@Test
104-
public void setConnectionTimeoutAsMinusOne() {
105-
setupConnectionTimeout(Duration.ofNanos(-1));
104+
public void setServerConnectionTimeoutAsMinusOne() {
105+
setupServerConnectionTimeout(Duration.ofNanos(-1));
106106
NettyReactiveWebServerFactory factory = mock(NettyReactiveWebServerFactory.class);
107107
this.customizer.customize(factory);
108108
verifyConnectionTimeout(factory, 0);
109109
}
110110

111+
@Test
112+
public void setServerConnectionTimeout() {
113+
setupServerConnectionTimeout(Duration.ofSeconds(1));
114+
NettyReactiveWebServerFactory factory = mock(NettyReactiveWebServerFactory.class);
115+
this.customizer.customize(factory);
116+
verifyConnectionTimeout(factory, 1000);
117+
}
118+
111119
@Test
112120
public void setConnectionTimeout() {
113121
setupConnectionTimeout(Duration.ofSeconds(1));
@@ -131,10 +139,16 @@ private void verifyConnectionTimeout(NettyReactiveWebServerFactory factory, Inte
131139
assertThat(options).containsEntry(ChannelOption.CONNECT_TIMEOUT_MILLIS, expected);
132140
}
133141

134-
private void setupConnectionTimeout(Duration connectionTimeout) {
142+
private void setupServerConnectionTimeout(Duration connectionTimeout) {
135143
this.serverProperties.setUseForwardHeaders(null);
136144
this.serverProperties.setMaxHttpHeaderSize(null);
137145
this.serverProperties.setConnectionTimeout(connectionTimeout);
138146
}
139147

148+
private void setupConnectionTimeout(Duration connectionTimeout) {
149+
this.serverProperties.setUseForwardHeaders(null);
150+
this.serverProperties.setMaxHttpHeaderSize(null);
151+
this.serverProperties.getNetty().setConnectionTimeout(connectionTimeout);
152+
}
153+
140154
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizerTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,14 @@ public void customMaxSwallowSize() {
151151
.getMaxSwallowSize()).isEqualTo(DataSize.ofMegabytes(10).toBytes()));
152152
}
153153

154+
@Test
155+
public void customConnectionTimeout() {
156+
bind("server.tomcat.connection-timeout=30s");
157+
customizeAndRunServer((server) -> assertThat(
158+
((AbstractProtocol<?>) server.getTomcat().getConnector().getProtocolHandler()).getConnectionTimeout())
159+
.isEqualTo(30000));
160+
}
161+
154162
@Test
155163
public void customRemoteIpValve() {
156164
bind("server.tomcat.remote-ip-header=x-my-remote-ip-header",

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizerTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,13 @@ public void customMaxHttpHeaderSizeIgnoredIfZero() {
135135

136136
@Test
137137
public void customConnectionTimeout() {
138-
bind("server.connection-timeout=100");
138+
bind("server.undertow.no-request-timeout=1m");
139139
Builder builder = Undertow.builder();
140140
ConfigurableUndertowWebServerFactory factory = mockFactory(builder);
141141
this.customizer.customize(factory);
142142
OptionMap map = ((OptionMap.Builder) ReflectionTestUtils.getField(builder, "serverOptions")).getMap();
143143
assertThat(map.contains(UndertowOptions.NO_REQUEST_TIMEOUT)).isTrue();
144-
assertThat(map.get(UndertowOptions.NO_REQUEST_TIMEOUT)).isEqualTo(100);
144+
assertThat(map.get(UndertowOptions.NO_REQUEST_TIMEOUT)).isEqualTo(60000);
145145
}
146146

147147
private ConfigurableUndertowWebServerFactory mockFactory(Builder builder) {

spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,10 @@ Rather, pick only the properties that you need.
207207
server.jetty.accesslog.log-server=false # Enable logging of the request hostname.
208208
server.jetty.accesslog.retention-period=31 # Number of days before rotated log files are deleted.
209209
server.jetty.accesslog.time-zone=GMT # Timezone of the request log.
210+
server.jetty.connection-idle-timeout= # Time that the connection can be idle before it is closed.
210211
server.jetty.max-http-post-size=200000B # Maximum size of the HTTP post or put content.
211212
server.jetty.selectors=-1 # Number of selector threads to use. When the value is -1, the default, the number of selectors is derived from the operating environment.
213+
server.netty.connection-timeout= # Connection timeout of the Netty channel.
212214
server.max-http-header-size=8KB # Maximum size of the HTTP message header.
213215
server.port=8080 # Server HTTP port.
214216
server.server-header= # Value to use for the Server response header (if empty, no header is sent).
@@ -259,6 +261,7 @@ Rather, pick only the properties that you need.
259261
server.tomcat.additional-tld-skip-patterns= # Comma-separated list of additional patterns that match jars to ignore for TLD scanning.
260262
server.tomcat.background-processor-delay=10s # Delay between the invocation of backgroundProcess methods. If a duration suffix is not specified, seconds will be used.
261263
server.tomcat.basedir= # Tomcat base directory. If not specified, a temporary directory is used.
264+
server.tomcat.connection-timeout= # Amount of time the connector will wait, after accepting a connection, for the request URI line to be presented.
262265
server.tomcat.internal-proxies=10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|\\
263266
192\\.168\\.\\d{1,3}\\.\\d{1,3}|\\
264267
169\\.254\\.\\d{1,3}\\.\\d{1,3}|\\
@@ -293,6 +296,7 @@ Rather, pick only the properties that you need.
293296
server.undertow.eager-filter-init=true # Whether servlet filters should be initialized on startup.
294297
server.undertow.io-threads= # Number of I/O threads to create for the worker. The default is derived from the number of available processors.
295298
server.undertow.max-http-post-size=-1B # Maximum size of the HTTP post content. When the value is -1, the default, the size is unlimited.
299+
server.undertow.no-request-timeout= # Amount of time a connection can sit idle without processing a request, before it is closed by the server.
296300
server.undertow.worker-threads= # Number of worker threads. The default is 8 times the number of I/O threads.
297301
298302
# FREEMARKER ({spring-boot-autoconfigure-module-code}/freemarker/FreeMarkerProperties.java[FreeMarkerProperties])

0 commit comments

Comments
 (0)