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 09975bb2d3b..570464db5bb 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1343,6 +1343,12 @@ public class ServerProperties { */ private DataSize maxInitialLineLength = DataSize.ofKilobytes(4); + /** + * Maximum number of requests that can be made per connection. By default, a + * connection serves unlimited number of requests. + */ + private Integer maxKeepAliveRequests; + /** * Whether to validate headers when decoding requests. */ @@ -1394,6 +1400,14 @@ public class ServerProperties { this.maxInitialLineLength = maxInitialLineLength; } + public Integer getMaxKeepAliveRequests() { + return this.maxKeepAliveRequests; + } + + public void setMaxKeepAliveRequests(Integer maxKeepAliveRequests) { + this.maxKeepAliveRequests = maxKeepAliveRequests; + } + public boolean isValidateHeaders() { return this.validateHeaders; } 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 5309f5ee1e3..1776b8c97b4 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -62,6 +62,8 @@ public class NettyWebServerFactoryCustomizer .to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout)); propertyMapper.from(nettyProperties::getIdleTimeout).whenNonNull() .to((idleTimeout) -> customizeIdleTimeout(factory, idleTimeout)); + propertyMapper.from(nettyProperties::getMaxKeepAliveRequests) + .to((maxKeepAliveRequests) -> customizeMaxKeepAliveRequests(factory, maxKeepAliveRequests)); customizeRequestDecoder(factory, propertyMapper); } @@ -104,4 +106,8 @@ public class NettyWebServerFactoryCustomizer factory.addServerCustomizers((httpServer) -> httpServer.idleTimeout(idleTimeout)); } + private void customizeMaxKeepAliveRequests(NettyReactiveWebServerFactory factory, int maxKeepAliveRequests) { + factory.addServerCustomizers((httpServer) -> httpServer.maxKeepAliveRequests(maxKeepAliveRequests)); + } + } 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 c2efe4e3f68..9dc2a9f2114 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -339,6 +339,12 @@ class ServerPropertiesTests { assertThat(this.properties.getNetty().getIdleTimeout()).isEqualTo(Duration.ofSeconds(10)); } + @Test + void testCustomizeNettyMaxKeepAliveRequests() { + bind("server.netty.max-keep-alive-requests", "100"); + assertThat(this.properties.getNetty().getMaxKeepAliveRequests()).isEqualTo(100); + } + @Test void tomcatAcceptCountMatchesProtocolDefault() throws Exception { assertThat(this.properties.getTomcat().getAcceptCount()).isEqualTo(getDefaultProtocol().getAcceptCount()); 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 1ea1a95cc46..9304603f380 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,6 +48,7 @@ import static org.mockito.Mockito.verify; * * @author Brian Clozel * @author Artsiom Yudovin + * @author Leo Li */ @ExtendWith(MockitoExtension.class) class NettyWebServerFactoryCustomizerTests { @@ -117,6 +118,14 @@ class NettyWebServerFactoryCustomizerTests { verifyIdleTimeout(factory, Duration.ofSeconds(1)); } + @Test + void setMaxKeepAliveRequests() { + this.serverProperties.getNetty().setMaxKeepAliveRequests(100); + NettyReactiveWebServerFactory factory = mock(NettyReactiveWebServerFactory.class); + this.customizer.customize(factory); + verifyMaxKeepAliveRequests(factory, 100); + } + @Test void configureHttpRequestDecoder() { ServerProperties.Netty nettyProperties = this.serverProperties.getNetty(); @@ -162,4 +171,12 @@ class NettyWebServerFactoryCustomizerTests { assertThat(idleTimeout).isEqualTo(expected); } + private void verifyMaxKeepAliveRequests(NettyReactiveWebServerFactory factory, int expected) { + verify(factory, times(2)).addServerCustomizers(this.customizerCaptor.capture()); + NettyServerCustomizer serverCustomizer = this.customizerCaptor.getAllValues().get(0); + HttpServer httpServer = serverCustomizer.apply(HttpServer.create()); + int maxKeepAliveRequests = httpServer.configuration().maxKeepAliveRequests(); + assertThat(maxKeepAliveRequests).isEqualTo(expected); + } + }