Merge branch '1.5.x' into 2.0.x

This commit is contained in:
Andy Wilkinson
2018-09-07 10:53:02 +01:00
4 changed files with 223 additions and 44 deletions

View File

@@ -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;
@@ -412,22 +413,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.
@@ -438,7 +439,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
@@ -449,20 +450,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
@@ -660,7 +661,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.
@@ -671,7 +672,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.
@@ -795,17 +796,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;
@@ -1006,27 +1009,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;

View File

@@ -16,19 +16,48 @@
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.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@@ -123,13 +152,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/");
@@ -184,6 +206,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<Throwable> failure = new AtomicReference<Throwable>();
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<String, Object> body = new LinkedMultiValueMap<String, Object>();
StringBuilder data = new StringBuilder();
for (int i = 0; i < 250000; i++) {
data.append("a");
}
body.add("data", data.toString());
HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<MultiValueMap<String, Object>>(
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));
}

View File

@@ -175,7 +175,7 @@ public class TomcatWebServerFactoryCustomizerTests {
public void defaultBackgroundProcessorDelay() {
TomcatWebServer server = customizeAndGetServer();
assertThat(server.getTomcat().getEngine().getBackgroundProcessorDelay())
.isEqualTo(30);
.isEqualTo(10);
}
@Test