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 0142e34a6916..73f684f62ed1 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 @@ -1148,6 +1148,11 @@ public static class Jetty { */ private DataSize maxHttpFormPostSize = DataSize.ofBytes(200000); + /** + * Maximum number of form keys. + */ + private int maxFormKeys = 1000; + /** * Time that the connection can be idle before it is closed. */ @@ -1180,6 +1185,14 @@ public void setMaxHttpFormPostSize(DataSize maxHttpFormPostSize) { this.maxHttpFormPostSize = maxHttpFormPostSize; } + public int getMaxFormKeys() { + return this.maxFormKeys; + } + + public void setMaxFormKeys(int maxFormKeys) { + this.maxFormKeys = maxFormKeys; + } + public Duration getConnectionIdleTimeout() { return this.connectionIdleTimeout; } 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 c12333dc9cfc..59387856bb8d 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 @@ -19,6 +19,7 @@ import java.time.Duration; import java.util.Arrays; import java.util.List; +import java.util.function.Consumer; import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.server.AbstractConnector; @@ -93,7 +94,11 @@ public void customize(ConfigurableJettyWebServerFactory factory) { map.from(properties::getMaxHttpFormPostSize) .asInt(DataSize::toBytes) .when(this::isPositive) - .to((maxHttpFormPostSize) -> customizeMaxHttpFormPostSize(factory, maxHttpFormPostSize)); + .to((maxHttpFormPostSize) -> customizeServletContextHandler(factory, contextHandler -> contextHandler.setMaxFormContentSize(maxHttpFormPostSize))); + map.from(properties::getMaxFormKeys) + .when(this::isPositive) + .to((maxFormKeys) -> customizeServletContextHandler(factory, contextHandler -> contextHandler.setMaxFormKeys(maxFormKeys))); + map.from(properties::getConnectionIdleTimeout).to((idleTimeout) -> customizeIdleTimeout(factory, idleTimeout)); map.from(properties::getAccesslog) .when(ServerProperties.Jetty.Accesslog::isEnabled) @@ -122,29 +127,29 @@ private void customizeIdleTimeout(ConfigurableJettyWebServerFactory factory, Dur }); } - private void customizeMaxHttpFormPostSize(ConfigurableJettyWebServerFactory factory, int maxHttpFormPostSize) { + private void customizeServletContextHandler(ConfigurableJettyWebServerFactory factory, Consumer customFunc) { factory.addServerCustomizers(new JettyServerCustomizer() { @Override public void customize(Server server) { - setHandlerMaxHttpFormPostSize(server.getHandlers()); + acceptCustomizeServletContextHandler(server.getHandlers()); } - private void setHandlerMaxHttpFormPostSize(List handlers) { + private void acceptCustomizeServletContextHandler(List handlers) { for (Handler handler : handlers) { - setHandlerMaxHttpFormPostSize(handler); + acceptCustomizeServletContextHandler(handler); } } - private void setHandlerMaxHttpFormPostSize(Handler handler) { + private void acceptCustomizeServletContextHandler(Handler handler) { if (handler instanceof ServletContextHandler contextHandler) { - contextHandler.setMaxFormContentSize(maxHttpFormPostSize); + customFunc.accept(contextHandler); } else if (handler instanceof Handler.Wrapper wrapper) { - setHandlerMaxHttpFormPostSize(wrapper.getHandler()); + acceptCustomizeServletContextHandler(wrapper.getHandler()); } else if (handler instanceof Handler.Collection collection) { - setHandlerMaxHttpFormPostSize(collection.getHandlers()); + acceptCustomizeServletContextHandler(collection.getHandlers()); } } 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 0bfeacf8ec67..01baba2092d6 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 @@ -465,6 +465,15 @@ void jettyMaxHttpFormPostSizeMatchesDefault() { .isEqualTo(((ServletContextHandler) server.getHandler()).getMaxFormContentSize()); } + @Test + void jettyMaxFormKeysMatchesDefault() { + JettyServletWebServerFactory jettyFactory = new JettyServletWebServerFactory(0); + JettyWebServer jetty = (JettyWebServer) jettyFactory.getWebServer(); + Server server = jetty.getServer(); + assertThat(this.properties.getJetty().getMaxFormKeys()) + .isEqualTo(((ServletContextHandler) server.getHandler()).getMaxFormKeys()); + } + @Test void undertowMaxHttpPostSizeMatchesDefault() { assertThat(this.properties.getUndertow().getMaxHttpPostSize().toBytes()) 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 eef94bb88a20..70a2b2c3e9ef 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 @@ -26,6 +26,7 @@ import java.util.concurrent.SynchronousQueue; import java.util.function.Function; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.server.AbstractConnector; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.CustomRequestLog; @@ -324,6 +325,23 @@ void customIdleTimeout() { assertThat(timeouts).containsOnly(60000L); } + @Test + void customMaxFormKeys() { + bind("server.jetty.max-form-keys=2048"); + JettyWebServer server = customizeAndGetServer(); + List maxFormKeys = getMaxFormKeys(server); + assertThat(maxFormKeys).containsOnly(2048); + } + + private List getMaxFormKeys(JettyWebServer server) { + server.start(); + server.stop(); + return server.getServer().getHandlers().stream() + .filter(handler -> handler instanceof ServletContextHandler) + .map(handler -> ((ServletContextHandler) handler).getMaxFormKeys()) + .toList(); + } + private List connectorsIdleTimeouts(JettyWebServer server) { // Start (and directly stop) server to have connectors available server.start();