diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java index 8706a4cc65..dc7935ee21 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java @@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.web; import java.io.File; import java.net.InetAddress; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.ArrayList; @@ -310,22 +311,22 @@ public class ServerProperties { * is not specified, seconds will be used. */ @DurationUnit(ChronoUnit.SECONDS) - private Duration backgroundProcessorDelay = Duration.ofSeconds(30); + private Duration backgroundProcessorDelay = Duration.ofSeconds(10); /** * Maximum number of worker threads. */ - private int maxThreads = 0; + private int maxThreads = 200; /** * Minimum number of worker threads. */ - private int minSpareThreads = 0; + private int minSpareThreads = 10; /** * Maximum size, in bytes, of the HTTP post content. */ - private int maxHttpPostSize = 0; + private int maxHttpPostSize = 2097152; /** * Maximum size, in bytes, of the HTTP message header. @@ -341,7 +342,7 @@ public class ServerProperties { * Whether requests to the context root should be redirected by appending a / to * the path. */ - private Boolean redirectContextRoot; + private Boolean redirectContextRoot = true; /** * Whether HTTP 1.1 and later location headers generated by a call to sendRedirect @@ -352,20 +353,20 @@ public class ServerProperties { /** * Character encoding to use to decode the URI. */ - private Charset uriEncoding; + private Charset uriEncoding = StandardCharsets.UTF_8; /** * Maximum number of connections that the server accepts and processes at any * given time. Once the limit has been reached, the operating system may still * accept connections based on the "acceptCount" property. */ - private int maxConnections = 0; + private int maxConnections = 10000; /** * Maximum queue length for incoming connection requests when all possible request * processing threads are in use. */ - private int acceptCount = 0; + private int acceptCount = 100; /** * Comma-separated list of additional patterns that match jars to ignore for TLD @@ -574,7 +575,7 @@ public class ServerProperties { * Whether to defer inclusion of the date stamp in the file name until rotate * time. */ - private boolean renameOnRotate; + private boolean renameOnRotate = false; /** * Date format to place in the log file name. @@ -585,7 +586,7 @@ public class ServerProperties { * Set request attributes for the IP address, Hostname, protocol, and port * used for the request. */ - private boolean requestAttributesEnabled; + private boolean requestAttributesEnabled = false; /** * Whether to buffer output such that it is flushed only periodically. @@ -722,17 +723,19 @@ public class ServerProperties { /** * Maximum size, in bytes, of the HTTP post or put content. */ - private int maxHttpPostSize = 0; // bytes + private int maxHttpPostSize = 200000; // bytes /** - * Number of acceptor threads to use. + * Number of acceptor threads to use. When the value is -1, the default, the + * number of acceptors is derived from the operating environment. */ - private Integer acceptors; + private Integer acceptors = -1; /** - * Number of selector threads to use. + * Number of selector threads to use. When the value is -1, the default, the + * number of selectors is derived from the operating environment. */ - private Integer selectors; + private Integer selectors = -1; public Accesslog getAccesslog() { return this.accesslog; @@ -933,27 +936,31 @@ public class ServerProperties { public static class Undertow { /** - * Maximum size, in bytes, of the HTTP post content. + * Maximum size, in bytes, of the HTTP post content. When the value is -1, the + * default, the size is unlimited. */ - private long maxHttpPostSize = 0; // bytes + private long maxHttpPostSize = -1; // bytes /** - * Size of each buffer, in bytes. + * Size of each buffer, in bytes. The default is derived from the maximum amount + * of memory that is available to the JVM. */ private Integer bufferSize; /** - * Number of I/O threads to create for the worker. + * Number of I/O threads to create for the worker. The default is derived from the + * number of available processors. */ private Integer ioThreads; /** - * Number of worker threads. + * Number of worker threads. The default is 8 times the number of I/O threads. */ private Integer workerThreads; /** - * Whether to allocate buffers outside the Java heap. + * Whether to allocate buffers outside the Java heap. The default is derived from + * the maximum amount of memory that is available to the JVM. */ private Boolean directBuffers; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java index e442224a40..090c9a3587 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java @@ -16,20 +16,49 @@ package org.springframework.boot.autoconfigure.web; +import java.io.IOException; import java.net.InetAddress; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import io.undertow.UndertowOptions; +import org.apache.catalina.connector.Connector; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.core.StandardEngine; +import org.apache.catalina.valves.AccessLogValve; +import org.apache.coyote.AbstractProtocol; +import org.eclipse.jetty.server.HttpChannel; +import org.eclipse.jetty.server.Request; import org.junit.Test; import org.springframework.boot.context.properties.bind.Bindable; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.context.properties.source.ConfigurationPropertySource; import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; +import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; +import org.springframework.boot.web.embedded.jetty.JettyWebServer; +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.servlet.ServletContextInitializer; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.util.unit.DataSize; +import org.springframework.web.client.ResponseErrorHandler; +import org.springframework.web.client.RestTemplate; import static org.assertj.core.api.Assertions.assertThat; @@ -108,13 +137,6 @@ public class ServerPropertiesTests { .isEqualTo(Duration.ofSeconds(10)); } - @Test - public void redirectContextRootIsNotConfiguredByDefault() { - bind(new HashMap<>()); - ServerProperties.Tomcat tomcat = this.properties.getTomcat(); - assertThat(tomcat.getRedirectContextRoot()).isNull(); - } - @Test public void testTrailingSlashOfContextPathIsRemoved() { bind("server.servlet.context-path", "/foo/"); @@ -177,6 +199,156 @@ public class ServerPropertiesTests { assertThat(jetty.getAccesslog().isAppend()).isTrue(); } + @Test + public void tomcatAcceptCountMatchesProtocolDefault() throws Exception { + assertThat(this.properties.getTomcat().getAcceptCount()) + .isEqualTo(getDefaultProtocol().getAcceptCount()); + } + + @Test + public void tomcatMaxConnectionsMatchesProtocolDefault() throws Exception { + assertThat(this.properties.getTomcat().getMaxConnections()) + .isEqualTo(getDefaultProtocol().getMaxConnections()); + } + + @Test + public void tomcatMaxThreadsMatchesProtocolDefault() throws Exception { + assertThat(this.properties.getTomcat().getMaxThreads()) + .isEqualTo(getDefaultProtocol().getMaxThreads()); + } + + @Test + public void tomcatMinSpareThreadsMatchesProtocolDefault() throws Exception { + assertThat(this.properties.getTomcat().getMinSpareThreads()) + .isEqualTo(getDefaultProtocol().getMinSpareThreads()); + } + + @Test + public void tomcatMaxHttpPostSizeMatchesConnectorDefault() throws Exception { + assertThat(this.properties.getTomcat().getMaxHttpPostSize()) + .isEqualTo(getDefaultConnector().getMaxPostSize()); + } + + @Test + public void tomcatBackgroundProcessorDelayMatchesEngineDefault() { + assertThat(this.properties.getTomcat().getBackgroundProcessorDelay()).isEqualTo( + Duration.ofSeconds((new StandardEngine().getBackgroundProcessorDelay()))); + } + + @Test + public void tomcatUriEncodingMatchesConnectorDefault() throws Exception { + assertThat(this.properties.getTomcat().getUriEncoding().name()) + .isEqualTo(getDefaultConnector().getURIEncoding()); + } + + @Test + public void tomcatRedirectContextRootMatchesDefault() { + assertThat(this.properties.getTomcat().getRedirectContextRoot()) + .isEqualTo(new StandardContext().getMapperContextRootRedirectEnabled()); + } + + @Test + public void tomcatAccessLogRenameOnRotateMatchesDefault() { + assertThat(this.properties.getTomcat().getAccesslog().isRenameOnRotate()) + .isEqualTo(new AccessLogValve().isRenameOnRotate()); + } + + @Test + public void tomcatAccessLogRequestAttributesEnabledMatchesDefault() { + assertThat( + this.properties.getTomcat().getAccesslog().isRequestAttributesEnabled()) + .isEqualTo(new AccessLogValve().getRequestAttributesEnabled()); + } + + @Test + public void jettyMaxHttpPostSizeMatchesDefault() throws Exception { + JettyServletWebServerFactory jettyFactory = new JettyServletWebServerFactory(0); + JettyWebServer jetty = (JettyWebServer) jettyFactory + .getWebServer(new ServletContextInitializer() { + + @Override + public void onStartup(ServletContext servletContext) + throws ServletException { + servletContext.addServlet("formPost", new HttpServlet() { + + @Override + protected void doPost(HttpServletRequest req, + HttpServletResponse resp) + throws ServletException, IOException { + req.getParameterMap(); + } + + }).addMapping("/form"); + } + + }); + jetty.start(); + org.eclipse.jetty.server.Connector connector = jetty.getServer() + .getConnectors()[0]; + final AtomicReference failure = new AtomicReference(); + connector.addBean(new HttpChannel.Listener() { + + @Override + public void onDispatchFailure(Request request, Throwable ex) { + failure.set(ex); + } + + }); + try { + RestTemplate template = new RestTemplate(); + template.setErrorHandler(new ResponseErrorHandler() { + + @Override + public boolean hasError(ClientHttpResponse response) throws IOException { + return false; + } + + @Override + public void handleError(ClientHttpResponse response) throws IOException { + + } + + }); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + MultiValueMap body = new LinkedMultiValueMap(); + StringBuilder data = new StringBuilder(); + for (int i = 0; i < 250000; i++) { + data.append("a"); + } + body.add("data", data.toString()); + HttpEntity> entity = new HttpEntity>( + body, headers); + template.postForEntity( + URI.create("http://localhost:" + jetty.getPort() + "/form"), entity, + Void.class); + assertThat(failure.get()).isNotNull(); + String message = failure.get().getCause().getMessage(); + int defaultMaxPostSize = Integer + .valueOf(message.substring(message.lastIndexOf(' ')).trim()); + assertThat(this.properties.getJetty().getMaxHttpPostSize()) + .isEqualTo(defaultMaxPostSize); + } + finally { + jetty.stop(); + } + } + + @Test + public void undertowMaxHttpPostSizeMatchesDefault() { + assertThat(this.properties.getUndertow().getMaxHttpPostSize()) + .isEqualTo(UndertowOptions.DEFAULT_MAX_ENTITY_SIZE); + } + + private Connector getDefaultConnector() throws Exception { + return new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL); + } + + private AbstractProtocol getDefaultProtocol() throws Exception { + return (AbstractProtocol) Class + .forName(TomcatServletWebServerFactory.DEFAULT_PROTOCOL).newInstance(); + } + private void bind(String name, String value) { bind(Collections.singletonMap(name, value)); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizerTests.java index 30cfca3e0d..3f0b835265 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizerTests.java @@ -235,7 +235,7 @@ public class TomcatWebServerFactoryCustomizerTests { public void defaultBackgroundProcessorDelay() { TomcatWebServer server = customizeAndGetServer(); assertThat(server.getTomcat().getEngine().getBackgroundProcessorDelay()) - .isEqualTo(30); + .isEqualTo(10); } @Test diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 79ff56cd0a..846f23e0e7 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -192,7 +192,7 @@ content into your application. Rather, pick only the properties that you need. server.error.path=/error # Path of the error controller. server.error.whitelabel.enabled=true # Whether to enable the default error page displayed in browsers in case of a server error. server.http2.enabled=false # Whether to enable HTTP/2 support, if the current environment supports it. - server.jetty.acceptors= # Number of acceptor threads to use. + server.jetty.acceptors=-1 # Number of acceptor threads to use. When the value is -1, the default, the number of acceptors is derived from the operating environment. server.jetty.accesslog.append=false # Append to log. server.jetty.accesslog.date-format=dd/MMM/yyyy:HH:mm:ss Z # Timestamp format of the request log. server.jetty.accesslog.enabled=false # Enable access log. @@ -205,8 +205,8 @@ content into your application. Rather, pick only the properties that you need. server.jetty.accesslog.log-server=false # Enable logging of the request hostname. server.jetty.accesslog.retention-period=31 # Number of days before rotated log files are deleted. server.jetty.accesslog.time-zone=GMT # Timezone of the request log. - server.jetty.max-http-post-size=0 # Maximum size, in bytes, of the HTTP post or put content. - server.jetty.selectors= # Number of selector threads to use. + server.jetty.max-http-post-size=200000 # Maximum size, in bytes, of the HTTP post or put content. + 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. server.max-http-header-size=8KB # Maximum size of the HTTP message header. server.port=8080 # Server HTTP port. server.server-header= # Value to use for the Server response header (if empty, no header is sent). @@ -243,7 +243,7 @@ content into your application. Rather, pick only the properties that you need. server.ssl.trust-store-password= # Password used to access the trust store. server.ssl.trust-store-provider= # Provider for the trust store. server.ssl.trust-store-type= # Type of the trust store. - server.tomcat.accept-count=0 # Maximum queue length for incoming connection requests when all possible request processing threads are in use. + server.tomcat.accept-count=100 # Maximum queue length for incoming connection requests when all possible request processing threads are in use. server.tomcat.accesslog.buffered=true # Whether to buffer output such that it is flushed only periodically. server.tomcat.accesslog.directory=logs # Directory in which log files are created. Can be absolute or relative to the Tomcat base dir. server.tomcat.accesslog.enabled=false # Enable access log. @@ -255,7 +255,7 @@ content into your application. Rather, pick only the properties that you need. server.tomcat.accesslog.rotate=true # Whether to enable access log rotation. server.tomcat.accesslog.suffix=.log # Log file name suffix. server.tomcat.additional-tld-skip-patterns= # Comma-separated list of additional patterns that match jars to ignore for TLD scanning. - server.tomcat.background-processor-delay=30s # Delay between the invocation of backgroundProcess methods. If a duration suffix is not specified, seconds will be used. + server.tomcat.background-processor-delay=10 # Delay in seconds between the invocation of backgroundProcess methods. server.tomcat.basedir= # Tomcat base directory. If not specified, a temporary directory is used. server.tomcat.internal-proxies=10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|\\ 192\\.168\\.\\d{1,3}\\.\\d{1,3}|\\ @@ -264,15 +264,15 @@ content into your application. Rather, pick only the properties that you need. 172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|\\ 172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|\\ 172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3} # Regular expression matching trusted IP addresses. - server.tomcat.max-connections=0 # Maximum number of connections that the server accepts and processes at any given time. - server.tomcat.max-http-post-size=0 # Maximum size, in bytes, of the HTTP post content. + server.tomcat.max-connections=10000 # Maximum number of connections that the server will accept and process at any given time. + server.tomcat.max-http-post-size=2097152 # Maximum size in bytes of the HTTP post content. server.tomcat.max-swallow-size=2MB # Maximum amount of request body to swallow. - server.tomcat.max-threads=0 # Maximum number of worker threads. - server.tomcat.min-spare-threads=0 # Minimum number of worker threads. + server.tomcat.max-threads=200 # Maximum amount of worker threads. + server.tomcat.min-spare-threads=10 # Minimum amount of worker threads. server.tomcat.port-header=X-Forwarded-Port # Name of the HTTP header used to override the original port value. server.tomcat.protocol-header= # Header that holds the incoming protocol, usually named "X-Forwarded-Proto". server.tomcat.protocol-header-https-value=https # Value of the protocol header indicating whether the incoming request uses SSL. - server.tomcat.redirect-context-root= # Whether requests to the context root should be redirected by appending a / to the path. + server.tomcat.redirect-context-root=true # Whether requests to the context root should be redirected by appending a / to the path. server.tomcat.remote-ip-header= # Name of the HTTP header from which the remote IP is extracted. For instance, `X-FORWARDED-FOR`. server.tomcat.resource.allow-caching=true # Whether static resource caching is permitted for this web application. server.tomcat.resource.cache-ttl= # Time-to-live of the static resource cache. @@ -285,11 +285,11 @@ content into your application. Rather, pick only the properties that you need. server.undertow.accesslog.rotate=true # Whether to enable access log rotation. server.undertow.accesslog.suffix=log # Log file name suffix. server.undertow.buffer-size= # Size of each buffer, in bytes. - server.undertow.direct-buffers= # Whether to allocate buffers outside the Java heap. - server.undertow.io-threads= # Number of I/O threads to create for the worker. + server.undertow.direct-buffers= # Allocate buffers outside the Java heap. The default is derived from the maximum amount of memory that is available to the JVM. server.undertow.eager-filter-init=true # Whether servlet filters should be initialized on startup. - server.undertow.max-http-post-size=0 # Maximum size, in bytes, of the HTTP post content. - server.undertow.worker-threads= # Number of worker threads. + server.undertow.io-threads= # Number of I/O threads to create for the worker. The default is derived from the number of available processors. + server.undertow.max-http-post-size=-1 # Maximum size in bytes of the HTTP post content. When the value is -1, the default, the size is unlimited. + server.undertow.worker-threads= # Number of worker threads. The default is 8 times the number of I/O threads. # FREEMARKER ({sc-spring-boot-autoconfigure}/freemarker/FreeMarkerProperties.{sc-ext}[FreeMarkerProperties]) spring.freemarker.allow-request-override=false # Whether HttpServletRequest attributes are allowed to override (hide) controller generated model attributes of the same name.