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 cc12b2efb3..adaa23ad78 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 @@ -67,6 +67,11 @@ public class NettyWebServerFactoryCustomizer .to((idleTimeout) -> customizeIdleTimeout(factory, idleTimeout)); propertyMapper.from(nettyProperties::getMaxKeepAliveRequests) .to((maxKeepAliveRequests) -> customizeMaxKeepAliveRequests(factory, maxKeepAliveRequests)); + if (this.serverProperties.getHttp2() != null && this.serverProperties.getHttp2().isEnabled()) { + propertyMapper.from(this.serverProperties.getMaxHttpRequestHeaderSize()) + .whenNonNull() + .to((size) -> customizeHttp2MaxHeaderSize(factory, size.toBytes())); + } customizeRequestDecoder(factory, propertyMapper); } @@ -125,4 +130,9 @@ public class NettyWebServerFactoryCustomizer factory.addServerCustomizers((httpServer) -> httpServer.maxKeepAliveRequests(maxKeepAliveRequests)); } + private void customizeHttp2MaxHeaderSize(NettyReactiveWebServerFactory factory, long size) { + factory.addServerCustomizers( + ((httpServer) -> httpServer.http2Settings((settings) -> settings.maxHeaderListSize(size)))); + } + } 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 e2267a005a..8279b8ba3c 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 @@ -26,6 +26,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.junit.jupiter.MockitoExtension; +import reactor.netty.http.Http2SettingsSpec; import reactor.netty.http.server.HttpRequestDecoderSpec; import reactor.netty.http.server.HttpServer; @@ -126,9 +127,20 @@ class NettyWebServerFactoryCustomizerTests { verifyMaxKeepAliveRequests(factory, 100); } + @Test + void setHttp2MaxRequestHeaderSize() { + DataSize headerSize = DataSize.ofKilobytes(24); + this.serverProperties.getHttp2().setEnabled(true); + this.serverProperties.setMaxHttpRequestHeaderSize(headerSize); + NettyReactiveWebServerFactory factory = mock(NettyReactiveWebServerFactory.class); + this.customizer.customize(factory); + verifyHttp2MaxHeaderSize(factory, headerSize.toBytes()); + } + @Test void configureHttpRequestDecoder() { ServerProperties.Netty nettyProperties = this.serverProperties.getNetty(); + this.serverProperties.setMaxHttpRequestHeaderSize(DataSize.ofKilobytes(24)); nettyProperties.setValidateHeaders(false); nettyProperties.setInitialBufferSize(DataSize.ofBytes(512)); nettyProperties.setH2cMaxContentLength(DataSize.ofKilobytes(1)); @@ -137,10 +149,11 @@ class NettyWebServerFactoryCustomizerTests { NettyReactiveWebServerFactory factory = mock(NettyReactiveWebServerFactory.class); this.customizer.customize(factory); then(factory).should().addServerCustomizers(this.customizerCaptor.capture()); - NettyServerCustomizer serverCustomizer = this.customizerCaptor.getValue(); + NettyServerCustomizer serverCustomizer = this.customizerCaptor.getAllValues().get(0); HttpServer httpServer = serverCustomizer.apply(HttpServer.create()); HttpRequestDecoderSpec decoder = httpServer.configuration().decoder(); assertThat(decoder.validateHeaders()).isFalse(); + assertThat(decoder.maxHeaderSize()).isEqualTo(this.serverProperties.getMaxHttpRequestHeaderSize().toBytes()); assertThat(decoder.initialBufferSize()).isEqualTo(nettyProperties.getInitialBufferSize().toBytes()); assertThat(decoder.h2cMaxContentLength()).isEqualTo(nettyProperties.getH2cMaxContentLength().toBytes()); assertMaxChunkSize(nettyProperties, decoder); @@ -189,4 +202,12 @@ class NettyWebServerFactoryCustomizerTests { assertThat(maxKeepAliveRequests).isEqualTo(expected); } + private void verifyHttp2MaxHeaderSize(NettyReactiveWebServerFactory factory, long expected) { + then(factory).should(times(2)).addServerCustomizers(this.customizerCaptor.capture()); + NettyServerCustomizer serverCustomizer = this.customizerCaptor.getAllValues().get(0); + HttpServer httpServer = serverCustomizer.apply(HttpServer.create()); + Http2SettingsSpec decoder = httpServer.configuration().http2SettingsSpec(); + assertThat(decoder.maxHeaderListSize()).isEqualTo(expected); + } + }