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 4a344584a02..ccefe488db2 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 @@ -29,6 +29,7 @@ import java.util.Map; import java.util.TimeZone; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.DeprecatedConfigurationProperty; import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.boot.convert.DurationUnit; import org.springframework.boot.web.server.Compression; @@ -53,6 +54,7 @@ import org.springframework.util.unit.DataSize; * @author Aurélien Leboulanger * @author Brian Clozel * @author Olivier Lamy + * @author Chentao Qu */ @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) public class ServerProperties { @@ -81,9 +83,9 @@ public class ServerProperties { private String serverHeader; /** - * Maximum size, in bytes, of the HTTP message header. + * Maximum size of the HTTP message header. */ - private int maxHttpHeaderSize = 0; // bytes + private DataSize maxHttpHeaderSize = DataSize.ofKiloBytes(8); /** * Time that connectors wait for another HTTP request before closing the connection. @@ -141,11 +143,11 @@ public class ServerProperties { this.serverHeader = serverHeader; } - public int getMaxHttpHeaderSize() { + public DataSize getMaxHttpHeaderSize() { return this.maxHttpHeaderSize; } - public void setMaxHttpHeaderSize(int maxHttpHeaderSize) { + public void setMaxHttpHeaderSize(DataSize maxHttpHeaderSize) { this.maxHttpHeaderSize = maxHttpHeaderSize; } @@ -493,10 +495,17 @@ public class ServerProperties { this.maxConnections = maxConnections; } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "server.max-http-header-size") public int getMaxHttpHeaderSize() { return this.maxHttpHeaderSize; } + @Deprecated + public void setMaxHttpHeaderSize(int maxHttpHeaderSize) { + this.maxHttpHeaderSize = maxHttpHeaderSize; + } + public DataSize getMaxSwallowSize() { return this.maxSwallowSize; } @@ -505,10 +514,6 @@ public class ServerProperties { this.maxSwallowSize = maxSwallowSize; } - public void setMaxHttpHeaderSize(int maxHttpHeaderSize) { - this.maxHttpHeaderSize = maxHttpHeaderSize; - } - public int getAcceptCount() { return this.acceptCount; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java index c50a111c57e..5dab73bd836 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java @@ -36,6 +36,7 @@ import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.core.Ordered; import org.springframework.core.env.Environment; +import org.springframework.util.unit.DataSize; /** * Customization for Jetty-specific features common for both Servlet and Reactive servers. @@ -73,7 +74,8 @@ public class JettyWebServerFactoryCustomizer implements .to(factory::setAcceptors); propertyMapper.from(jettyProperties::getSelectors).whenNonNull() .to(factory::setSelectors); - propertyMapper.from(properties::getMaxHttpHeaderSize).when(this::isPositive) + propertyMapper.from(properties::getMaxHttpHeaderSize).whenNonNull() + .asInt(DataSize::toBytes) .to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory, maxHttpHeaderSize)); propertyMapper.from(jettyProperties::getMaxHttpPostSize).when(this::isPositive) @@ -133,7 +135,6 @@ public class JettyWebServerFactoryCustomizer implements private void customize(HttpConfiguration.ConnectionFactory factory) { HttpConfiguration configuration = factory.getHttpConfiguration(); configuration.setRequestHeaderSize(maxHttpHeaderSize); - configuration.setResponseHeaderSize(maxHttpHeaderSize); } }); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/NettyWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/NettyWebServerFactoryCustomizer.java index 74ff91f6834..eb226513c30 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/NettyWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/NettyWebServerFactoryCustomizer.java @@ -18,15 +18,19 @@ package org.springframework.boot.autoconfigure.web.embedded; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.cloud.CloudPlatform; +import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory; +import org.springframework.boot.web.embedded.netty.NettyServerCustomizer; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.core.Ordered; import org.springframework.core.env.Environment; +import org.springframework.util.unit.DataSize; /** * Customization for Netty-specific features. * * @author Brian Clozel + * @author Chentao Qu * @since 2.1.0 */ public class NettyWebServerFactoryCustomizer @@ -51,6 +55,11 @@ public class NettyWebServerFactoryCustomizer public void customize(NettyReactiveWebServerFactory factory) { factory.setUseForwardHeaders( getOrDeduceUseForwardHeaders(this.serverProperties, this.environment)); + PropertyMapper propertyMapper = PropertyMapper.get(); + propertyMapper.from(this.serverProperties::getMaxHttpHeaderSize).whenNonNull() + .asInt(DataSize::toBytes) + .to((maxHttpRequestHeaderSize) -> customizeMaxHttpHeaderSize(factory, + maxHttpRequestHeaderSize)); } private boolean getOrDeduceUseForwardHeaders(ServerProperties serverProperties, @@ -62,4 +71,11 @@ public class NettyWebServerFactoryCustomizer return platform != null && platform.isUsingForwardHeaders(); } + private void customizeMaxHttpHeaderSize(NettyReactiveWebServerFactory factory, + Integer maxHttpHeaderSize) { + factory.addServerCustomizers((NettyServerCustomizer) (httpServer) -> httpServer + .httpRequestDecoder((httpRequestDecoderSpec) -> httpRequestDecoderSpec + .maxHeaderSize(maxHttpHeaderSize))); + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java index 06a4dc21fcf..3bf75348d20 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java @@ -48,6 +48,7 @@ import org.springframework.util.unit.DataSize; * @author Stephane Nicoll * @author Phillip Webb * @author Artsiom Yudovin + * @author Chentao Qu * @since 2.0.0 */ public class TomcatWebServerFactoryCustomizer implements @@ -84,7 +85,8 @@ public class TomcatWebServerFactoryCustomizer implements tomcatProperties.getMaxThreads())); propertyMapper.from(tomcatProperties::getMinSpareThreads).when(this::isPositive) .to((minSpareThreads) -> customizeMinThreads(factory, minSpareThreads)); - propertyMapper.from(this::determineMaxHttpHeaderSize).when(this::isPositive) + propertyMapper.from(this::determineMaxHttpHeaderSize).whenNonNull() + .asInt(DataSize::toBytes) .to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory, maxHttpHeaderSize)); propertyMapper.from(tomcatProperties::getMaxSwallowSize).whenNonNull() @@ -114,10 +116,12 @@ public class TomcatWebServerFactoryCustomizer implements return value > 0; } - private int determineMaxHttpHeaderSize() { - return (this.serverProperties.getMaxHttpHeaderSize() > 0) - ? this.serverProperties.getMaxHttpHeaderSize() - : this.serverProperties.getTomcat().getMaxHttpHeaderSize(); + @SuppressWarnings("deprecation") + private DataSize determineMaxHttpHeaderSize() { + return isPositive(this.serverProperties.getTomcat().getMaxHttpHeaderSize()) + ? DataSize + .ofBytes(this.serverProperties.getTomcat().getMaxHttpHeaderSize()) + : this.serverProperties.getMaxHttpHeaderSize(); } private void customizeAcceptCount(ConfigurableTomcatWebServerFactory factory, diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java index 985128cfd3d..c08ef547db4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java @@ -27,6 +27,7 @@ import org.springframework.boot.web.embedded.undertow.ConfigurableUndertowWebSer import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.core.Ordered; import org.springframework.core.env.Environment; +import org.springframework.util.unit.DataSize; /** * Customization for Undertow-specific features common for both Servlet and Reactive @@ -83,7 +84,8 @@ public class UndertowWebServerFactoryCustomizer implements .to(factory::setAccessLogRotate); propertyMapper.from(this::getOrDeduceUseForwardHeaders) .to(factory::setUseForwardHeaders); - propertyMapper.from(properties::getMaxHttpHeaderSize).when(this::isPositive) + propertyMapper.from(properties::getMaxHttpHeaderSize).whenNonNull() + .asInt(DataSize::toBytes) .to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory, maxHttpHeaderSize)); propertyMapper.from(undertowProperties::getMaxHttpPostSize).when(this::isPositive) 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 771c3b646ca..0b4b3276dc0 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 @@ -29,6 +29,7 @@ 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.util.unit.DataSize; import static org.assertj.core.api.Assertions.assertThat; @@ -135,8 +136,16 @@ public class ServerPropertiesTests { @Test public void testCustomizeHeaderSize() { - bind("server.max-http-header-size", "9999"); - assertThat(this.properties.getMaxHttpHeaderSize()).isEqualTo(9999); + bind("server.max-http-header-size", "1MB"); + assertThat(this.properties.getMaxHttpHeaderSize()) + .isEqualTo(DataSize.ofMegaBytes(1)); + } + + @Test + public void testCustomizeHeaderSizeUseBytesByDefault() { + bind("server.max-http-header-size", "1024"); + assertThat(this.properties.getMaxHttpHeaderSize()) + .isEqualTo(DataSize.ofKiloBytes(1)); } @Test diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java index 1899eab287b..a81312e5df8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java @@ -21,6 +21,9 @@ import java.io.IOException; import java.util.Locale; import java.util.TimeZone; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConfiguration.ConnectionFactory; import org.eclipse.jetty.server.NCSARequestLog; import org.eclipse.jetty.server.RequestLog; import org.junit.Before; @@ -140,6 +143,21 @@ public class JettyWebServerFactoryCustomizerTests { verify(factory).setUseForwardHeaders(true); } + @Test + public void customizeMaxHttpHeaderSize() { + bind("server.max-http-header-size=2048"); + JettyWebServer server = customizeAndGetServer(); + for (Connector connector : server.getServer().getConnectors()) { + connector.getConnectionFactories().stream() + .filter((factory) -> factory instanceof ConnectionFactory) + .forEach((cf) -> { + ConnectionFactory factory = (ConnectionFactory) cf; + HttpConfiguration configuration = factory.getHttpConfiguration(); + assertThat(configuration.getRequestHeaderSize()).isEqualTo(2048); + }); + } + } + private void bind(String... inlinedProperties) { TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, inlinedProperties); 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 165f99f3e88..60687e555be 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 @@ -121,6 +121,24 @@ public class TomcatWebServerFactoryCustomizerTests { .isEqualTo(10000)); } + @Test + public void customMaxHttpHeaderSize() { + bind("server.max-http-header-size=1KB"); + customizeAndRunServer((server) -> assertThat(((AbstractHttp11Protocol) server + .getTomcat().getConnector().getProtocolHandler()).getMaxHttpHeaderSize()) + .isEqualTo(DataSize.ofKiloBytes(1).toBytes())); + } + + @Test + @Deprecated + public void customMaxHttpHeaderSizeWithDeprecatedProperty() { + bind("server.max-http-header-size=4KB", + "server.tomcat.max-http-header-size=1024"); + customizeAndRunServer((server) -> assertThat(((AbstractHttp11Protocol) server + .getTomcat().getConnector().getProtocolHandler()).getMaxHttpHeaderSize()) + .isEqualTo(DataSize.ofKiloBytes(1).toBytes())); + } + @Test public void customMaxSwallowSize() { bind("server.tomcat.max-swallow-size=10MB"); 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 b24fb70352e..ad6cdfe920b 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 @@ -207,7 +207,7 @@ content into your application. Rather, pick only the properties that you need. 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.max-http-header-size=0 # Maximum size, in bytes, of the HTTP message header. + 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). server.use-forward-headers= # Whether X-Forwarded-* headers should be applied to the HttpRequest. @@ -265,7 +265,6 @@ content into your application. Rather, pick only the properties that you need. 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-header-size=0 # Maximum size, in bytes, of the HTTP message header. server.tomcat.max-http-post-size=0 # 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.