Merge pull request #14234 from qct:max-header

* pr/14234:
  Polish "Align max HTTP header size configuration"
  Align max HTTP header size configuration
This commit is contained in:
Stephane Nicoll 2018-09-04 10:53:54 +02:00
commit 9496e87b07
9 changed files with 92 additions and 20 deletions

View File

@ -29,6 +29,7 @@ import java.util.Map;
import java.util.TimeZone; import java.util.TimeZone;
import org.springframework.boot.context.properties.ConfigurationProperties; 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.context.properties.NestedConfigurationProperty;
import org.springframework.boot.convert.DurationUnit; import org.springframework.boot.convert.DurationUnit;
import org.springframework.boot.web.server.Compression; import org.springframework.boot.web.server.Compression;
@ -53,6 +54,7 @@ import org.springframework.util.unit.DataSize;
* @author Aurélien Leboulanger * @author Aurélien Leboulanger
* @author Brian Clozel * @author Brian Clozel
* @author Olivier Lamy * @author Olivier Lamy
* @author Chentao Qu
*/ */
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties { public class ServerProperties {
@ -81,9 +83,9 @@ public class ServerProperties {
private String serverHeader; 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. * Time that connectors wait for another HTTP request before closing the connection.
@ -141,11 +143,11 @@ public class ServerProperties {
this.serverHeader = serverHeader; this.serverHeader = serverHeader;
} }
public int getMaxHttpHeaderSize() { public DataSize getMaxHttpHeaderSize() {
return this.maxHttpHeaderSize; return this.maxHttpHeaderSize;
} }
public void setMaxHttpHeaderSize(int maxHttpHeaderSize) { public void setMaxHttpHeaderSize(DataSize maxHttpHeaderSize) {
this.maxHttpHeaderSize = maxHttpHeaderSize; this.maxHttpHeaderSize = maxHttpHeaderSize;
} }
@ -493,10 +495,17 @@ public class ServerProperties {
this.maxConnections = maxConnections; this.maxConnections = maxConnections;
} }
@Deprecated
@DeprecatedConfigurationProperty(replacement = "server.max-http-header-size")
public int getMaxHttpHeaderSize() { public int getMaxHttpHeaderSize() {
return this.maxHttpHeaderSize; return this.maxHttpHeaderSize;
} }
@Deprecated
public void setMaxHttpHeaderSize(int maxHttpHeaderSize) {
this.maxHttpHeaderSize = maxHttpHeaderSize;
}
public DataSize getMaxSwallowSize() { public DataSize getMaxSwallowSize() {
return this.maxSwallowSize; return this.maxSwallowSize;
} }
@ -505,10 +514,6 @@ public class ServerProperties {
this.maxSwallowSize = maxSwallowSize; this.maxSwallowSize = maxSwallowSize;
} }
public void setMaxHttpHeaderSize(int maxHttpHeaderSize) {
this.maxHttpHeaderSize = maxHttpHeaderSize;
}
public int getAcceptCount() { public int getAcceptCount() {
return this.acceptCount; return this.acceptCount;
} }

View File

@ -36,6 +36,7 @@ import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer;
import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.util.unit.DataSize;
/** /**
* Customization for Jetty-specific features common for both Servlet and Reactive servers. * Customization for Jetty-specific features common for both Servlet and Reactive servers.
@ -73,7 +74,8 @@ public class JettyWebServerFactoryCustomizer implements
.to(factory::setAcceptors); .to(factory::setAcceptors);
propertyMapper.from(jettyProperties::getSelectors).whenNonNull() propertyMapper.from(jettyProperties::getSelectors).whenNonNull()
.to(factory::setSelectors); .to(factory::setSelectors);
propertyMapper.from(properties::getMaxHttpHeaderSize).when(this::isPositive) propertyMapper.from(properties::getMaxHttpHeaderSize).whenNonNull()
.asInt(DataSize::toBytes)
.to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory, .to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory,
maxHttpHeaderSize)); maxHttpHeaderSize));
propertyMapper.from(jettyProperties::getMaxHttpPostSize).when(this::isPositive) propertyMapper.from(jettyProperties::getMaxHttpPostSize).when(this::isPositive)
@ -133,7 +135,6 @@ public class JettyWebServerFactoryCustomizer implements
private void customize(HttpConfiguration.ConnectionFactory factory) { private void customize(HttpConfiguration.ConnectionFactory factory) {
HttpConfiguration configuration = factory.getHttpConfiguration(); HttpConfiguration configuration = factory.getHttpConfiguration();
configuration.setRequestHeaderSize(maxHttpHeaderSize); configuration.setRequestHeaderSize(maxHttpHeaderSize);
configuration.setResponseHeaderSize(maxHttpHeaderSize);
} }
}); });

View File

@ -18,15 +18,19 @@ package org.springframework.boot.autoconfigure.web.embedded;
import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.cloud.CloudPlatform; 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.NettyReactiveWebServerFactory;
import org.springframework.boot.web.embedded.netty.NettyServerCustomizer;
import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.util.unit.DataSize;
/** /**
* Customization for Netty-specific features. * Customization for Netty-specific features.
* *
* @author Brian Clozel * @author Brian Clozel
* @author Chentao Qu
* @since 2.1.0 * @since 2.1.0
*/ */
public class NettyWebServerFactoryCustomizer public class NettyWebServerFactoryCustomizer
@ -51,6 +55,11 @@ public class NettyWebServerFactoryCustomizer
public void customize(NettyReactiveWebServerFactory factory) { public void customize(NettyReactiveWebServerFactory factory) {
factory.setUseForwardHeaders( factory.setUseForwardHeaders(
getOrDeduceUseForwardHeaders(this.serverProperties, this.environment)); 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, private boolean getOrDeduceUseForwardHeaders(ServerProperties serverProperties,
@ -62,4 +71,11 @@ public class NettyWebServerFactoryCustomizer
return platform != null && platform.isUsingForwardHeaders(); return platform != null && platform.isUsingForwardHeaders();
} }
private void customizeMaxHttpHeaderSize(NettyReactiveWebServerFactory factory,
Integer maxHttpHeaderSize) {
factory.addServerCustomizers((NettyServerCustomizer) (httpServer) -> httpServer
.httpRequestDecoder((httpRequestDecoderSpec) -> httpRequestDecoderSpec
.maxHeaderSize(maxHttpHeaderSize)));
}
} }

View File

@ -48,6 +48,7 @@ import org.springframework.util.unit.DataSize;
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Phillip Webb * @author Phillip Webb
* @author Artsiom Yudovin * @author Artsiom Yudovin
* @author Chentao Qu
* @since 2.0.0 * @since 2.0.0
*/ */
public class TomcatWebServerFactoryCustomizer implements public class TomcatWebServerFactoryCustomizer implements
@ -84,7 +85,8 @@ public class TomcatWebServerFactoryCustomizer implements
tomcatProperties.getMaxThreads())); tomcatProperties.getMaxThreads()));
propertyMapper.from(tomcatProperties::getMinSpareThreads).when(this::isPositive) propertyMapper.from(tomcatProperties::getMinSpareThreads).when(this::isPositive)
.to((minSpareThreads) -> customizeMinThreads(factory, minSpareThreads)); .to((minSpareThreads) -> customizeMinThreads(factory, minSpareThreads));
propertyMapper.from(this::determineMaxHttpHeaderSize).when(this::isPositive) propertyMapper.from(this::determineMaxHttpHeaderSize).whenNonNull()
.asInt(DataSize::toBytes)
.to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory, .to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory,
maxHttpHeaderSize)); maxHttpHeaderSize));
propertyMapper.from(tomcatProperties::getMaxSwallowSize).whenNonNull() propertyMapper.from(tomcatProperties::getMaxSwallowSize).whenNonNull()
@ -114,10 +116,12 @@ public class TomcatWebServerFactoryCustomizer implements
return value > 0; return value > 0;
} }
private int determineMaxHttpHeaderSize() { @SuppressWarnings("deprecation")
return (this.serverProperties.getMaxHttpHeaderSize() > 0) private DataSize determineMaxHttpHeaderSize() {
? this.serverProperties.getMaxHttpHeaderSize() return isPositive(this.serverProperties.getTomcat().getMaxHttpHeaderSize())
: this.serverProperties.getTomcat().getMaxHttpHeaderSize(); ? DataSize
.ofBytes(this.serverProperties.getTomcat().getMaxHttpHeaderSize())
: this.serverProperties.getMaxHttpHeaderSize();
} }
private void customizeAcceptCount(ConfigurableTomcatWebServerFactory factory, private void customizeAcceptCount(ConfigurableTomcatWebServerFactory factory,

View File

@ -27,6 +27,7 @@ import org.springframework.boot.web.embedded.undertow.ConfigurableUndertowWebSer
import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.util.unit.DataSize;
/** /**
* Customization for Undertow-specific features common for both Servlet and Reactive * Customization for Undertow-specific features common for both Servlet and Reactive
@ -83,7 +84,8 @@ public class UndertowWebServerFactoryCustomizer implements
.to(factory::setAccessLogRotate); .to(factory::setAccessLogRotate);
propertyMapper.from(this::getOrDeduceUseForwardHeaders) propertyMapper.from(this::getOrDeduceUseForwardHeaders)
.to(factory::setUseForwardHeaders); .to(factory::setUseForwardHeaders);
propertyMapper.from(properties::getMaxHttpHeaderSize).when(this::isPositive) propertyMapper.from(properties::getMaxHttpHeaderSize).whenNonNull()
.asInt(DataSize::toBytes)
.to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory, .to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory,
maxHttpHeaderSize)); maxHttpHeaderSize));
propertyMapper.from(undertowProperties::getMaxHttpPostSize).when(this::isPositive) propertyMapper.from(undertowProperties::getMaxHttpPostSize).when(this::isPositive)

View File

@ -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.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource; import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import org.springframework.util.unit.DataSize;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -135,8 +136,16 @@ public class ServerPropertiesTests {
@Test @Test
public void testCustomizeHeaderSize() { public void testCustomizeHeaderSize() {
bind("server.max-http-header-size", "9999"); bind("server.max-http-header-size", "1MB");
assertThat(this.properties.getMaxHttpHeaderSize()).isEqualTo(9999); 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 @Test

View File

@ -21,6 +21,9 @@ import java.io.IOException;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone; 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.NCSARequestLog;
import org.eclipse.jetty.server.RequestLog; import org.eclipse.jetty.server.RequestLog;
import org.junit.Before; import org.junit.Before;
@ -140,6 +143,21 @@ public class JettyWebServerFactoryCustomizerTests {
verify(factory).setUseForwardHeaders(true); 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) { private void bind(String... inlinedProperties) {
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment,
inlinedProperties); inlinedProperties);

View File

@ -121,6 +121,24 @@ public class TomcatWebServerFactoryCustomizerTests {
.isEqualTo(10000)); .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 @Test
public void customMaxSwallowSize() { public void customMaxSwallowSize() {
bind("server.tomcat.max-swallow-size=10MB"); bind("server.tomcat.max-swallow-size=10MB");

View File

@ -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.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.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.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.port=8080 # Server HTTP port.
server.server-header= # Value to use for the Server response header (if empty, no header is sent). 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. 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\\.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. 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-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-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-swallow-size=2MB # Maximum amount of request body to swallow.
server.tomcat.max-threads=0 # Maximum number of worker threads. server.tomcat.max-threads=0 # Maximum number of worker threads.