Merge branch '2.1.x'

Closes gh-18618
This commit is contained in:
Brian Clozel
2019-10-16 14:09:46 +02:00
9 changed files with 137 additions and 10 deletions

View File

@@ -118,6 +118,8 @@ public class ServerProperties {
private final Jetty jetty = new Jetty();
private final Netty netty = new Netty();
private final Undertow undertow = new Undertow();
public Integer getPort() {
@@ -163,10 +165,14 @@ public class ServerProperties {
this.maxHttpHeaderSize = maxHttpHeaderSize;
}
@Deprecated
@DeprecatedConfigurationProperty(
reason = "Each server behaves differently. Use server specific properties instead.")
public Duration getConnectionTimeout() {
return this.connectionTimeout;
}
@Deprecated
public void setConnectionTimeout(Duration connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
@@ -203,6 +209,10 @@ public class ServerProperties {
return this.jetty;
}
public Netty getNetty() {
return this.netty;
}
public Undertow getUndertow() {
return this.undertow;
}
@@ -414,6 +424,12 @@ public class ServerProperties {
*/
private List<Character> relaxedQueryChars = new ArrayList<>();
/**
* Amount of time the connector will wait, after accepting a connection, for the
* request URI line to be presented.
*/
private Duration connectionTimeout;
/**
* Static resource configuration.
*/
@@ -596,6 +612,14 @@ public class ServerProperties {
this.relaxedQueryChars = relaxedQueryChars;
}
public Duration getConnectionTimeout() {
return this.connectionTimeout;
}
public void setConnectionTimeout(Duration connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
public Resource getResource() {
return this.resource;
}
@@ -934,6 +958,11 @@ public class ServerProperties {
*/
private Duration idleTimeout = Duration.ofMillis(60000);
/**
* Time that the connection can be idle before it is closed.
*/
private Duration connectionIdleTimeout;
public Accesslog getAccesslog() {
return this.accesslog;
}
@@ -986,6 +1015,14 @@ public class ServerProperties {
return this.idleTimeout;
}
public Duration getConnectionIdleTimeout() {
return this.connectionIdleTimeout;
}
public void setConnectionIdleTimeout(Duration connectionIdleTimeout) {
this.connectionIdleTimeout = connectionIdleTimeout;
}
/**
* Jetty access log properties.
*/
@@ -1118,6 +1155,26 @@ public class ServerProperties {
}
/**
* Netty properties.
*/
public static class Netty {
/**
* Connection timeout of the Netty channel.
*/
private Duration connectionTimeout;
public Duration getConnectionTimeout() {
return this.connectionTimeout;
}
public void setConnectionTimeout(Duration connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
}
/**
* Undertow properties.
*/
@@ -1200,6 +1257,12 @@ public class ServerProperties {
*/
private boolean alwaysSetKeepAlive = true;
/**
* Amount of time a connection can sit idle without processing a request, before
* it is closed by the server.
*/
private Duration noRequestTimeout;
private final Accesslog accesslog = new Accesslog();
private final Options options = new Options();
@@ -1308,6 +1371,14 @@ public class ServerProperties {
this.alwaysSetKeepAlive = alwaysSetKeepAlive;
}
public Duration getNoRequestTimeout() {
return this.noRequestTimeout;
}
public void setNoRequestTimeout(Duration noRequestTimeout) {
this.noRequestTimeout = noRequestTimeout;
}
public Accesslog getAccesslog() {
return this.accesslog;
}

View File

@@ -89,7 +89,9 @@ public class JettyWebServerFactoryCustomizer
propertyMapper.from(jettyProperties::getIdleTimeout).whenNonNull().asInt(Duration::toMillis).to(
(idleTimeout) -> customizeThreadPool(factory, (threadPool) -> threadPool.setIdleTimeout(idleTimeout)));
propertyMapper.from(properties::getConnectionTimeout).whenNonNull()
.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
.to((connectionTimeout) -> customizeIdleTimeout(factory, connectionTimeout));
propertyMapper.from(jettyProperties::getConnectionIdleTimeout).whenNonNull()
.to((idleTimeout) -> customizeIdleTimeout(factory, idleTimeout));
propertyMapper.from(jettyProperties::getAccesslog).when(ServerProperties.Jetty.Accesslog::isEnabled)
.to((accesslog) -> customizeAccessLog(factory, accesslog));
}
@@ -106,7 +108,7 @@ public class JettyWebServerFactoryCustomizer
return this.serverProperties.getForwardHeadersStrategy().equals(ServerProperties.ForwardHeadersStrategy.NATIVE);
}
private void customizeConnectionTimeout(ConfigurableJettyWebServerFactory factory, Duration connectionTimeout) {
private void customizeIdleTimeout(ConfigurableJettyWebServerFactory factory, Duration connectionTimeout) {
factory.addServerCustomizers((server) -> {
for (org.eclipse.jetty.server.Connector connector : server.getConnectors()) {
if (connector instanceof AbstractConnector) {

View File

@@ -61,6 +61,9 @@ public class NettyWebServerFactoryCustomizer
propertyMapper.from(this.serverProperties::getMaxHttpHeaderSize)
.to((maxHttpRequestHeaderSize) -> customizeMaxHttpHeaderSize(factory, maxHttpRequestHeaderSize));
propertyMapper.from(this.serverProperties::getConnectionTimeout)
.to((connectionTimeout) -> customizeGenericConnectionTimeout(factory, connectionTimeout));
ServerProperties.Netty nettyProperties = this.serverProperties.getNetty();
propertyMapper.from(nettyProperties::getConnectionTimeout).whenNonNull()
.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
}
@@ -77,7 +80,7 @@ public class NettyWebServerFactoryCustomizer
(httpRequestDecoderSpec) -> httpRequestDecoderSpec.maxHeaderSize((int) maxHttpHeaderSize.toBytes())));
}
private void customizeConnectionTimeout(NettyReactiveWebServerFactory factory, Duration connectionTimeout) {
private void customizeGenericConnectionTimeout(NettyReactiveWebServerFactory factory, Duration connectionTimeout) {
if (!connectionTimeout.isZero()) {
long timeoutMillis = connectionTimeout.isNegative() ? 0 : connectionTimeout.toMillis();
factory.addServerCustomizers((httpServer) -> httpServer.tcpConfiguration((tcpServer) -> tcpServer
@@ -85,4 +88,9 @@ public class NettyWebServerFactoryCustomizer
}
}
private void customizeConnectionTimeout(NettyReactiveWebServerFactory factory, Duration connectionTimeout) {
factory.addServerCustomizers((httpServer) -> httpServer.tcpConfiguration((tcpServer) -> tcpServer
.selectorOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, (int) connectionTimeout.toMillis())));
}
}

View File

@@ -99,6 +99,8 @@ public class TomcatWebServerFactoryCustomizer
propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull().to(factory::setUriEncoding);
propertyMapper.from(properties::getConnectionTimeout).whenNonNull()
.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
propertyMapper.from(tomcatProperties::getConnectionTimeout).whenNonNull()
.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive)
.to((maxConnections) -> customizeMaxConnections(factory, maxConnections));
propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive)

View File

@@ -101,6 +101,8 @@ public class UndertowWebServerFactoryCustomizer
map.from(properties::isDecodeUrl).to(options.server(UndertowOptions.DECODE_URL));
map.from(properties::getUrlCharset).as(Charset::name).to(options.server(UndertowOptions.URL_CHARSET));
map.from(properties::isAlwaysSetKeepAlive).to(options.server(UndertowOptions.ALWAYS_SET_KEEP_ALIVE));
map.from(properties::getNoRequestTimeout).asInt(Duration::toMillis)
.to(options.server(UndertowOptions.NO_REQUEST_TIMEOUT));
map.from(properties.getOptions()::getServer).to(options.forEach(options::server));
map.from(properties.getOptions()::getSocket).to(options.forEach(options::socket));
}

View File

@@ -19,8 +19,11 @@ package org.springframework.boot.autoconfigure.web.embedded;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.jetty.server.AbstractConnector;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.CustomRequestLog;
import org.eclipse.jetty.server.HttpConfiguration;
@@ -182,6 +185,23 @@ class JettyWebServerFactoryCustomizerTests {
assertThat(requestHeaderSizes).containsOnly(8192);
}
@Test
void customIdleTimeout() {
bind("server.jetty.idle-timeout=60s");
JettyWebServer server = customizeAndGetServer();
List<Long> timeouts = connectorsIdleTimeouts(server);
assertThat(timeouts).containsOnly(60000L);
}
private List<Long> connectorsIdleTimeouts(JettyWebServer server) {
// Start (and directly stop) server to have connectors available
server.start();
server.stop();
return Arrays.stream(server.getServer().getConnectors())
.filter((connector) -> connector instanceof AbstractConnector).map(Connector::getIdleTimeout)
.collect(Collectors.toList());
}
private List<Integer> getRequestHeaderSizes(JettyWebServer server) {
List<Integer> requestHeaderSizes = new ArrayList<>();
// Start (and directly stop) server to have connectors available

View File

@@ -93,21 +93,29 @@ class NettyWebServerFactoryCustomizerTests {
}
@Test
void setConnectionTimeoutAsZero() {
setupConnectionTimeout(Duration.ZERO);
void setServerConnectionTimeoutAsZero() {
setupServerConnectionTimeout(Duration.ZERO);
NettyReactiveWebServerFactory factory = mock(NettyReactiveWebServerFactory.class);
this.customizer.customize(factory);
verifyConnectionTimeout(factory, null);
}
@Test
void setConnectionTimeoutAsMinusOne() {
setupConnectionTimeout(Duration.ofNanos(-1));
void setServerConnectionTimeoutAsMinusOne() {
setupServerConnectionTimeout(Duration.ofNanos(-1));
NettyReactiveWebServerFactory factory = mock(NettyReactiveWebServerFactory.class);
this.customizer.customize(factory);
verifyConnectionTimeout(factory, 0);
}
@Test
void setServerConnectionTimeout() {
setupServerConnectionTimeout(Duration.ofSeconds(1));
NettyReactiveWebServerFactory factory = mock(NettyReactiveWebServerFactory.class);
this.customizer.customize(factory);
verifyConnectionTimeout(factory, 1000);
}
@Test
void setConnectionTimeout() {
setupConnectionTimeout(Duration.ofSeconds(1));
@@ -131,10 +139,16 @@ class NettyWebServerFactoryCustomizerTests {
assertThat(options).containsEntry(ChannelOption.CONNECT_TIMEOUT_MILLIS, expected);
}
private void setupConnectionTimeout(Duration connectionTimeout) {
private void setupServerConnectionTimeout(Duration connectionTimeout) {
this.serverProperties.setUseForwardHeaders(null);
this.serverProperties.setMaxHttpHeaderSize(null);
this.serverProperties.setConnectionTimeout(connectionTimeout);
}
private void setupConnectionTimeout(Duration connectionTimeout) {
this.serverProperties.setUseForwardHeaders(null);
this.serverProperties.setMaxHttpHeaderSize(null);
this.serverProperties.getNetty().setConnectionTimeout(connectionTimeout);
}
}

View File

@@ -288,6 +288,14 @@ class TomcatWebServerFactoryCustomizerTests {
assertThat(this.serverProperties.getTomcat().getMinSpareThreads()).isEqualTo(10);
}
@Test
void customConnectionTimeout() {
bind("server.tomcat.connection-timeout=30s");
customizeAndRunServer((server) -> assertThat(
((AbstractProtocol<?>) server.getTomcat().getConnector().getProtocolHandler()).getConnectionTimeout())
.isEqualTo(30000));
}
@Test
void accessLogBufferingCanBeDisabled() {
bind("server.tomcat.accesslog.enabled=true", "server.tomcat.accesslog.buffered=false");

View File

@@ -110,8 +110,8 @@ class UndertowWebServerFactoryCustomizerTests {
@Test
void customConnectionTimeout() {
bind("server.connectionTimeout=100");
assertThat(boundServerOption(UndertowOptions.NO_REQUEST_TIMEOUT)).isEqualTo(100);
bind("server.undertow.no-request-timeout=1m");
assertThat(boundServerOption(UndertowOptions.NO_REQUEST_TIMEOUT)).isEqualTo(60000);
}
@Test