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 cd004a92c3..9740226d8c 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 @@ -1397,29 +1397,29 @@ public class ServerProperties { private Duration connectionTimeout; /** - * The maximum chunk size that can be decoded for the HTTP request. + * Maximum content length of an H2C upgrade request. */ - private DataSize maxChunkSize; + private DataSize h2cMaxContentLength = DataSize.ofBytes(0); /** - * The maximum length that can be decoded for the HTTP request's initial line. + * Initial buffer size for HTTP request decoding. */ - private DataSize maxInitialLineLength; + private DataSize initialBufferSize = DataSize.ofBytes(128); /** - * Configure whether or not to validate headers when decoding requests. + * Maximum chunk size that can be decoded for an HTTP request. */ - private Boolean validateHeaders; + private DataSize maxChunkSize = DataSize.ofKilobytes(8); /** - * The maximum length of the content of the HTTP/2.0 clear-text upgrade request. + * Maximum length that can be decoded for an HTTP request's initial line. */ - private DataSize h2cMaxContentLength; + private DataSize maxInitialLineLength = DataSize.ofKilobytes(4); /** - * The initial buffer size for HTTP request decoding. + * Whether to validate headers when decoding requests. */ - private DataSize initialBufferSize; + private boolean validateHeaders = true; public Duration getConnectionTimeout() { return this.connectionTimeout; @@ -1429,6 +1429,22 @@ public class ServerProperties { this.connectionTimeout = connectionTimeout; } + public DataSize getH2cMaxContentLength() { + return this.h2cMaxContentLength; + } + + public void setH2cMaxContentLength(DataSize h2cMaxContentLength) { + this.h2cMaxContentLength = h2cMaxContentLength; + } + + public DataSize getInitialBufferSize() { + return this.initialBufferSize; + } + + public void setInitialBufferSize(DataSize initialBufferSize) { + this.initialBufferSize = initialBufferSize; + } + public DataSize getMaxChunkSize() { return this.maxChunkSize; } @@ -1445,30 +1461,14 @@ public class ServerProperties { this.maxInitialLineLength = maxInitialLineLength; } - public Boolean getValidateHeaders() { + public boolean isValidateHeaders() { return this.validateHeaders; } - public void setValidateHeaders(Boolean validateHeaders) { + public void setValidateHeaders(boolean validateHeaders) { this.validateHeaders = validateHeaders; } - public DataSize getH2cMaxContentLength() { - return this.h2cMaxContentLength; - } - - public void setH2cMaxContentLength(DataSize h2cMaxContentLength) { - this.h2cMaxContentLength = h2cMaxContentLength; - } - - public DataSize getInitialBufferSize() { - return this.initialBufferSize; - } - - public void setInitialBufferSize(DataSize initialBufferSize) { - this.initialBufferSize = initialBufferSize; - } - } /** 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 4920057cdc..2e807bd6f9 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 @@ -17,8 +17,6 @@ package org.springframework.boot.autoconfigure.web.embedded; import java.time.Duration; -import java.util.Objects; -import java.util.stream.Stream; import io.netty.channel.ChannelOption; @@ -62,13 +60,7 @@ public class NettyWebServerFactoryCustomizer ServerProperties.Netty nettyProperties = this.serverProperties.getNetty(); propertyMapper.from(nettyProperties::getConnectionTimeout).whenNonNull() .to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout)); - if (Stream - .of(this.serverProperties.getMaxHttpHeaderSize(), nettyProperties.getH2cMaxContentLength(), - nettyProperties.getMaxChunkSize(), nettyProperties.getMaxInitialLineLength(), - nettyProperties.getValidateHeaders(), nettyProperties.getInitialBufferSize()) - .anyMatch(Objects::nonNull)) { - customizeRequestDecoder(factory, propertyMapper); - } + customizeRequestDecoder(factory, propertyMapper); } private boolean getOrDeduceUseForwardHeaders() { @@ -79,6 +71,11 @@ public class NettyWebServerFactoryCustomizer return this.serverProperties.getForwardHeadersStrategy().equals(ServerProperties.ForwardHeadersStrategy.NATIVE); } + private void customizeConnectionTimeout(NettyReactiveWebServerFactory factory, Duration connectionTimeout) { + factory.addServerCustomizers((httpServer) -> httpServer.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, + (int) connectionTimeout.toMillis())); + } + private void customizeRequestDecoder(NettyReactiveWebServerFactory factory, PropertyMapper propertyMapper) { factory.addServerCustomizers((httpServer) -> httpServer.httpRequestDecoder((httpRequestDecoderSpec) -> { propertyMapper.from(this.serverProperties.getMaxHttpHeaderSize()).whenNonNull() @@ -95,15 +92,10 @@ public class NettyWebServerFactoryCustomizer .h2cMaxContentLength((int) h2cMaxContentLength.toBytes())); propertyMapper.from(nettyProperties.getInitialBufferSize()).whenNonNull().to( (initialBufferSize) -> httpRequestDecoderSpec.initialBufferSize((int) initialBufferSize.toBytes())); - propertyMapper.from(nettyProperties.getValidateHeaders()).whenNonNull() + propertyMapper.from(nettyProperties.isValidateHeaders()).whenNonNull() .to(httpRequestDecoderSpec::validateHeaders); return httpRequestDecoderSpec; })); } - private void customizeConnectionTimeout(NettyReactiveWebServerFactory factory, Duration connectionTimeout) { - factory.addServerCustomizers((httpServer) -> httpServer.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, - (int) connectionTimeout.toMillis())); - } - } 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 c479305808..cbc47a09e5 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 @@ -43,6 +43,8 @@ import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.thread.ThreadPool; import org.junit.jupiter.api.Test; +import reactor.netty.http.HttpDecoderSpec; +import reactor.netty.http.server.HttpRequestDecoderSpec; import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.Accesslog; import org.springframework.boot.context.properties.bind.Bindable; @@ -533,6 +535,35 @@ class ServerPropertiesTests { .isEqualTo(UndertowOptions.DEFAULT_MAX_ENTITY_SIZE); } + @Test + void nettyMaxChunkSizeMatchesHttpDecoderSpecDefault() { + assertThat(this.properties.getNetty().getMaxChunkSize().toBytes()) + .isEqualTo(HttpDecoderSpec.DEFAULT_MAX_CHUNK_SIZE); + } + + @Test + void nettyMaxInitialLineLenghtMatchesHttpDecoderSpecDefault() { + assertThat(this.properties.getNetty().getMaxInitialLineLength().toBytes()) + .isEqualTo(HttpDecoderSpec.DEFAULT_MAX_INITIAL_LINE_LENGTH); + } + + @Test + void nettyValidateHeadersMatchesHttpDecoderSpecDefault() { + assertThat(this.properties.getNetty().isValidateHeaders()).isEqualTo(HttpDecoderSpec.DEFAULT_VALIDATE_HEADERS); + } + + @Test + void nettyH2cMaxContentLengthMatchesHttpDecoderSpecDefault() { + assertThat(this.properties.getNetty().getH2cMaxContentLength().toBytes()) + .isEqualTo(HttpRequestDecoderSpec.DEFAULT_H2C_MAX_CONTENT_LENGTH); + } + + @Test + void nettyInitialBufferSizeMatchesHttpDecoderSpecDefault() { + assertThat(this.properties.getNetty().getInitialBufferSize().toBytes()) + .isEqualTo(HttpDecoderSpec.DEFAULT_INITIAL_BUFFER_SIZE); + } + private Connector getDefaultConnector() throws Exception { return new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/NettyWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/NettyWebServerFactoryCustomizerTests.java index cba78dc385..884861cb7b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/NettyWebServerFactoryCustomizerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/NettyWebServerFactoryCustomizerTests.java @@ -110,9 +110,9 @@ class NettyWebServerFactoryCustomizerTests { } @Test - void setHttpRequestDecoder() { + void configureHttpRequestDecoder() { ServerProperties.Netty nettyProperties = this.serverProperties.getNetty(); - nettyProperties.setValidateHeaders(true); + nettyProperties.setValidateHeaders(false); nettyProperties.setInitialBufferSize(DataSize.ofBytes(512)); nettyProperties.setH2cMaxContentLength(DataSize.ofKilobytes(1)); nettyProperties.setMaxChunkSize(DataSize.ofKilobytes(16)); @@ -123,28 +123,20 @@ class NettyWebServerFactoryCustomizerTests { NettyServerCustomizer serverCustomizer = this.customizerCaptor.getValue(); HttpServer httpServer = serverCustomizer.apply(HttpServer.create()); HttpRequestDecoderSpec decoder = httpServer.configuration().decoder(); - assertThat(decoder.validateHeaders()).isTrue(); + assertThat(decoder.validateHeaders()).isFalse(); assertThat(decoder.initialBufferSize()).isEqualTo(nettyProperties.getInitialBufferSize().toBytes()); assertThat(decoder.h2cMaxContentLength()).isEqualTo(nettyProperties.getH2cMaxContentLength().toBytes()); assertThat(decoder.maxChunkSize()).isEqualTo(nettyProperties.getMaxChunkSize().toBytes()); assertThat(decoder.maxInitialLineLength()).isEqualTo(nettyProperties.getMaxInitialLineLength().toBytes()); } - @Test - void shouldNotSetAnyHttpRequestDecoderProperties() { - this.serverProperties.setMaxHttpHeaderSize(null); - NettyReactiveWebServerFactory factory = mock(NettyReactiveWebServerFactory.class); - this.customizer.customize(factory); - verify(factory, never()).addServerCustomizers(this.customizerCaptor.capture()); - } - private void verifyConnectionTimeout(NettyReactiveWebServerFactory factory, Integer expected) { if (expected == null) { verify(factory, never()).addServerCustomizers(any(NettyServerCustomizer.class)); return; } - verify(factory, times(1)).addServerCustomizers(this.customizerCaptor.capture()); - NettyServerCustomizer serverCustomizer = this.customizerCaptor.getValue(); + verify(factory, times(2)).addServerCustomizers(this.customizerCaptor.capture()); + NettyServerCustomizer serverCustomizer = this.customizerCaptor.getAllValues().get(0); HttpServer httpServer = serverCustomizer.apply(HttpServer.create()); Map, ?> options = httpServer.configuration().options(); assertThat(options.get(ChannelOption.CONNECT_TIMEOUT_MILLIS)).isEqualTo(expected);