Polish 'Add support for 'server.jetty.max-form-key' property'
See gh-42448
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 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.
|
||||
@@ -19,22 +19,23 @@ package org.springframework.boot.autoconfigure.web.embedded;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.server.AbstractConnector;
|
||||
import org.eclipse.jetty.server.ConnectionFactory;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.CustomRequestLog;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.RequestLogWriter;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.cloud.CloudPlatform;
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.boot.web.embedded.jetty.ConfigurableJettyWebServerFactory;
|
||||
import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.Environment;
|
||||
@@ -84,22 +85,21 @@ public class JettyWebServerFactoryCustomizer
|
||||
map.from(this.serverProperties::getMaxHttpRequestHeaderSize)
|
||||
.asInt(DataSize::toBytes)
|
||||
.when(this::isPositive)
|
||||
.to((maxHttpRequestHeaderSize) -> factory
|
||||
.addServerCustomizers(new MaxHttpRequestHeaderSizeCustomizer(maxHttpRequestHeaderSize)));
|
||||
.to(customizeHttpConfigurations(factory, HttpConfiguration::setRequestHeaderSize));
|
||||
map.from(properties::getMaxHttpResponseHeaderSize)
|
||||
.asInt(DataSize::toBytes)
|
||||
.when(this::isPositive)
|
||||
.to((maxHttpResponseHeaderSize) -> factory
|
||||
.addServerCustomizers(new MaxHttpResponseHeaderSizeCustomizer(maxHttpResponseHeaderSize)));
|
||||
.to(customizeHttpConfigurations(factory, HttpConfiguration::setResponseHeaderSize));
|
||||
map.from(properties::getMaxHttpFormPostSize)
|
||||
.asInt(DataSize::toBytes)
|
||||
.when(this::isPositive)
|
||||
.to((maxHttpFormPostSize) -> customizeServletContextHandler(factory, contextHandler -> contextHandler.setMaxFormContentSize(maxHttpFormPostSize)));
|
||||
.to(customizeServletContextHandler(factory, ServletContextHandler::setMaxFormContentSize));
|
||||
map.from(properties::getMaxFormKeys)
|
||||
.when(this::isPositive)
|
||||
.to((maxFormKeys) -> customizeServletContextHandler(factory, contextHandler -> contextHandler.setMaxFormKeys(maxFormKeys)));
|
||||
|
||||
map.from(properties::getConnectionIdleTimeout).to((idleTimeout) -> customizeIdleTimeout(factory, idleTimeout));
|
||||
.when(this::isPositive)
|
||||
.to(customizeServletContextHandler(factory, ServletContextHandler::setMaxFormKeys));
|
||||
map.from(properties::getConnectionIdleTimeout)
|
||||
.as(Duration::toMillis)
|
||||
.to(customizeAbstractConnectors(factory, AbstractConnector::setIdleTimeout));
|
||||
map.from(properties::getAccesslog)
|
||||
.when(ServerProperties.Jetty.Accesslog::isEnabled)
|
||||
.to((accesslog) -> customizeAccessLog(factory, accesslog));
|
||||
@@ -117,45 +117,65 @@ public class JettyWebServerFactoryCustomizer
|
||||
return this.serverProperties.getForwardHeadersStrategy().equals(ServerProperties.ForwardHeadersStrategy.NATIVE);
|
||||
}
|
||||
|
||||
private void customizeIdleTimeout(ConfigurableJettyWebServerFactory factory, Duration connectionTimeout) {
|
||||
factory.addServerCustomizers((server) -> {
|
||||
for (org.eclipse.jetty.server.Connector connector : server.getConnectors()) {
|
||||
if (connector instanceof AbstractConnector abstractConnector) {
|
||||
abstractConnector.setIdleTimeout(connectionTimeout.toMillis());
|
||||
}
|
||||
}
|
||||
private <T> Consumer<T> customizeHttpConfigurations(ConfigurableJettyWebServerFactory factory,
|
||||
BiConsumer<HttpConfiguration, T> action) {
|
||||
return customizeConnectionFactories(factory, HttpConfiguration.ConnectionFactory.class,
|
||||
(connectionFactory, value) -> action.accept(connectionFactory.getHttpConfiguration(), value));
|
||||
}
|
||||
|
||||
private <V, F> Consumer<V> customizeConnectionFactories(ConfigurableJettyWebServerFactory factory,
|
||||
Class<F> connectionFactoryType, BiConsumer<F, V> action) {
|
||||
return customizeConnectors(factory, Connector.class, (connector, value) -> {
|
||||
Stream<ConnectionFactory> connectionFactories = connector.getConnectionFactories().stream();
|
||||
forEach(connectionFactories, connectionFactoryType, action, value);
|
||||
});
|
||||
}
|
||||
|
||||
private void customizeServletContextHandler(ConfigurableJettyWebServerFactory factory, Consumer<ServletContextHandler> customFunc) {
|
||||
factory.addServerCustomizers(new JettyServerCustomizer() {
|
||||
|
||||
@Override
|
||||
public void customize(Server server) {
|
||||
acceptCustomizeServletContextHandler(server.getHandlers());
|
||||
}
|
||||
|
||||
private void acceptCustomizeServletContextHandler(List<Handler> handlers) {
|
||||
for (Handler handler : handlers) {
|
||||
acceptCustomizeServletContextHandler(handler);
|
||||
}
|
||||
}
|
||||
|
||||
private void acceptCustomizeServletContextHandler(Handler handler) {
|
||||
if (handler instanceof ServletContextHandler contextHandler) {
|
||||
customFunc.accept(contextHandler);
|
||||
}
|
||||
else if (handler instanceof Handler.Wrapper wrapper) {
|
||||
acceptCustomizeServletContextHandler(wrapper.getHandler());
|
||||
}
|
||||
else if (handler instanceof Handler.Collection collection) {
|
||||
acceptCustomizeServletContextHandler(collection.getHandlers());
|
||||
}
|
||||
}
|
||||
private <V> Consumer<V> customizeAbstractConnectors(ConfigurableJettyWebServerFactory factory,
|
||||
BiConsumer<AbstractConnector, V> action) {
|
||||
return customizeConnectors(factory, AbstractConnector.class, action);
|
||||
}
|
||||
|
||||
private <V, C> Consumer<V> customizeConnectors(ConfigurableJettyWebServerFactory factory, Class<C> connectorType,
|
||||
BiConsumer<C, V> action) {
|
||||
return (value) -> factory.addServerCustomizers((server) -> {
|
||||
Stream<Connector> connectors = Arrays.stream(server.getConnectors());
|
||||
forEach(connectors, connectorType, action, value);
|
||||
});
|
||||
}
|
||||
|
||||
private <V> Consumer<V> customizeServletContextHandler(ConfigurableJettyWebServerFactory factory,
|
||||
BiConsumer<ServletContextHandler, V> action) {
|
||||
return customizeHandlers(factory, ServletContextHandler.class, action);
|
||||
}
|
||||
|
||||
private <V, H> Consumer<V> customizeHandlers(ConfigurableJettyWebServerFactory factory, Class<H> handlerType,
|
||||
BiConsumer<H, V> action) {
|
||||
return (value) -> factory.addServerCustomizers((server) -> {
|
||||
List<Handler> handlers = server.getHandlers();
|
||||
forEachHandler(handlers, handlerType, action, value);
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <V, H> void forEachHandler(List<Handler> handlers, Class<H> handlerType, BiConsumer<H, V> action, V value) {
|
||||
for (Handler handler : handlers) {
|
||||
if (handlerType.isInstance(handler)) {
|
||||
action.accept((H) handler, value);
|
||||
}
|
||||
if (handler instanceof Handler.Wrapper wrapper) {
|
||||
forEachHandler(wrapper.getHandlers(), handlerType, action, value);
|
||||
}
|
||||
if (handler instanceof Handler.Collection collection) {
|
||||
forEachHandler(collection.getHandlers(), handlerType, action, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <T, V> void forEach(Stream<?> elements, Class<T> type, BiConsumer<T, V> action, V value) {
|
||||
elements.filter(type::isInstance).map(type::cast).forEach((element) -> action.accept(element, value));
|
||||
}
|
||||
|
||||
private void customizeAccessLog(ConfigurableJettyWebServerFactory factory,
|
||||
ServerProperties.Jetty.Accesslog properties) {
|
||||
factory.addServerCustomizers((server) -> {
|
||||
@@ -181,61 +201,10 @@ public class JettyWebServerFactoryCustomizer
|
||||
if (properties.getCustomFormat() != null) {
|
||||
return properties.getCustomFormat();
|
||||
}
|
||||
else if (ServerProperties.Jetty.Accesslog.FORMAT.EXTENDED_NCSA.equals(properties.getFormat())) {
|
||||
if (ServerProperties.Jetty.Accesslog.FORMAT.EXTENDED_NCSA.equals(properties.getFormat())) {
|
||||
return CustomRequestLog.EXTENDED_NCSA_FORMAT;
|
||||
}
|
||||
return CustomRequestLog.NCSA_FORMAT;
|
||||
}
|
||||
|
||||
private static class MaxHttpRequestHeaderSizeCustomizer implements JettyServerCustomizer {
|
||||
|
||||
private final int maxRequestHeaderSize;
|
||||
|
||||
MaxHttpRequestHeaderSizeCustomizer(int maxRequestHeaderSize) {
|
||||
this.maxRequestHeaderSize = maxRequestHeaderSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(Server server) {
|
||||
Arrays.stream(server.getConnectors()).forEach(this::customize);
|
||||
}
|
||||
|
||||
private void customize(org.eclipse.jetty.server.Connector connector) {
|
||||
connector.getConnectionFactories().forEach(this::customize);
|
||||
}
|
||||
|
||||
private void customize(ConnectionFactory factory) {
|
||||
if (factory instanceof HttpConfiguration.ConnectionFactory) {
|
||||
((HttpConfiguration.ConnectionFactory) factory).getHttpConfiguration()
|
||||
.setRequestHeaderSize(this.maxRequestHeaderSize);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MaxHttpResponseHeaderSizeCustomizer implements JettyServerCustomizer {
|
||||
|
||||
private final int maxResponseHeaderSize;
|
||||
|
||||
MaxHttpResponseHeaderSizeCustomizer(int maxResponseHeaderSize) {
|
||||
this.maxResponseHeaderSize = maxResponseHeaderSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(Server server) {
|
||||
Arrays.stream(server.getConnectors()).forEach(this::customize);
|
||||
}
|
||||
|
||||
private void customize(org.eclipse.jetty.server.Connector connector) {
|
||||
connector.getConnectionFactories().forEach(this::customize);
|
||||
}
|
||||
|
||||
private void customize(ConnectionFactory factory) {
|
||||
if (factory instanceof HttpConfiguration.ConnectionFactory httpConnectionFactory) {
|
||||
httpConnectionFactory.getHttpConfiguration().setResponseHeaderSize(this.maxResponseHeaderSize);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -471,7 +471,7 @@ class ServerPropertiesTests {
|
||||
JettyWebServer jetty = (JettyWebServer) jettyFactory.getWebServer();
|
||||
Server server = jetty.getServer();
|
||||
assertThat(this.properties.getJetty().getMaxFormKeys())
|
||||
.isEqualTo(((ServletContextHandler) server.getHandler()).getMaxFormKeys());
|
||||
.isEqualTo(((ServletContextHandler) server.getHandler()).getMaxFormKeys());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 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.
|
||||
@@ -329,23 +329,19 @@ class JettyWebServerFactoryCustomizerTests {
|
||||
void customMaxFormKeys() {
|
||||
bind("server.jetty.max-form-keys=2048");
|
||||
JettyWebServer server = customizeAndGetServer();
|
||||
List<Integer> maxFormKeys = getMaxFormKeys(server);
|
||||
startAndStopToMakeInternalsAvailable(server);
|
||||
List<Integer> maxFormKeys = server.getServer()
|
||||
.getHandlers()
|
||||
.stream()
|
||||
.filter(ServletContextHandler.class::isInstance)
|
||||
.map(ServletContextHandler.class::cast)
|
||||
.map(ServletContextHandler::getMaxFormKeys)
|
||||
.toList();
|
||||
assertThat(maxFormKeys).containsOnly(2048);
|
||||
}
|
||||
|
||||
private List<Integer> 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<Long> connectorsIdleTimeouts(JettyWebServer server) {
|
||||
// Start (and directly stop) server to have connectors available
|
||||
server.start();
|
||||
server.stop();
|
||||
startAndStopToMakeInternalsAvailable(server);
|
||||
return Arrays.stream(server.getServer().getConnectors())
|
||||
.filter((connector) -> connector instanceof AbstractConnector)
|
||||
.map(Connector::getIdleTimeout)
|
||||
@@ -362,9 +358,7 @@ class JettyWebServerFactoryCustomizerTests {
|
||||
|
||||
private List<Integer> getHeaderSizes(JettyWebServer server, Function<HttpConfiguration, Integer> provider) {
|
||||
List<Integer> requestHeaderSizes = new ArrayList<>();
|
||||
// Start (and directly stop) server to have connectors available
|
||||
server.start();
|
||||
server.stop();
|
||||
startAndStopToMakeInternalsAvailable(server);
|
||||
Connector[] connectors = server.getServer().getConnectors();
|
||||
for (Connector connector : connectors) {
|
||||
connector.getConnectionFactories()
|
||||
@@ -379,6 +373,11 @@ class JettyWebServerFactoryCustomizerTests {
|
||||
return requestHeaderSizes;
|
||||
}
|
||||
|
||||
private void startAndStopToMakeInternalsAvailable(JettyWebServer server) {
|
||||
server.start();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
private BlockingQueue<?> getQueue(ThreadPool threadPool) {
|
||||
return ReflectionTestUtils.invokeMethod(threadPool, "getQueue");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user