Skip to content

Commit 58a1b2b

Browse files
committed
Polish 'Add support for 'server.jetty.max-form-key' property'
See gh-42448
1 parent 6e885c4 commit 58a1b2b

File tree

3 files changed

+79
-111
lines changed

3 files changed

+79
-111
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,22 +19,23 @@
1919
import java.time.Duration;
2020
import java.util.Arrays;
2121
import java.util.List;
22+
import java.util.function.BiConsumer;
2223
import java.util.function.Consumer;
24+
import java.util.stream.Stream;
2325

2426
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
2527
import org.eclipse.jetty.server.AbstractConnector;
2628
import org.eclipse.jetty.server.ConnectionFactory;
29+
import org.eclipse.jetty.server.Connector;
2730
import org.eclipse.jetty.server.CustomRequestLog;
2831
import org.eclipse.jetty.server.Handler;
2932
import org.eclipse.jetty.server.HttpConfiguration;
3033
import org.eclipse.jetty.server.RequestLogWriter;
31-
import org.eclipse.jetty.server.Server;
3234

3335
import org.springframework.boot.autoconfigure.web.ServerProperties;
3436
import org.springframework.boot.cloud.CloudPlatform;
3537
import org.springframework.boot.context.properties.PropertyMapper;
3638
import org.springframework.boot.web.embedded.jetty.ConfigurableJettyWebServerFactory;
37-
import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer;
3839
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
3940
import org.springframework.core.Ordered;
4041
import org.springframework.core.env.Environment;
@@ -84,22 +85,21 @@ public void customize(ConfigurableJettyWebServerFactory factory) {
8485
map.from(this.serverProperties::getMaxHttpRequestHeaderSize)
8586
.asInt(DataSize::toBytes)
8687
.when(this::isPositive)
87-
.to((maxHttpRequestHeaderSize) -> factory
88-
.addServerCustomizers(new MaxHttpRequestHeaderSizeCustomizer(maxHttpRequestHeaderSize)));
88+
.to(customizeHttpConfigurations(factory, HttpConfiguration::setRequestHeaderSize));
8989
map.from(properties::getMaxHttpResponseHeaderSize)
9090
.asInt(DataSize::toBytes)
9191
.when(this::isPositive)
92-
.to((maxHttpResponseHeaderSize) -> factory
93-
.addServerCustomizers(new MaxHttpResponseHeaderSizeCustomizer(maxHttpResponseHeaderSize)));
92+
.to(customizeHttpConfigurations(factory, HttpConfiguration::setResponseHeaderSize));
9493
map.from(properties::getMaxHttpFormPostSize)
9594
.asInt(DataSize::toBytes)
9695
.when(this::isPositive)
97-
.to((maxHttpFormPostSize) -> customizeServletContextHandler(factory, contextHandler -> contextHandler.setMaxFormContentSize(maxHttpFormPostSize)));
96+
.to(customizeServletContextHandler(factory, ServletContextHandler::setMaxFormContentSize));
9897
map.from(properties::getMaxFormKeys)
99-
.when(this::isPositive)
100-
.to((maxFormKeys) -> customizeServletContextHandler(factory, contextHandler -> contextHandler.setMaxFormKeys(maxFormKeys)));
101-
102-
map.from(properties::getConnectionIdleTimeout).to((idleTimeout) -> customizeIdleTimeout(factory, idleTimeout));
98+
.when(this::isPositive)
99+
.to(customizeServletContextHandler(factory, ServletContextHandler::setMaxFormKeys));
100+
map.from(properties::getConnectionIdleTimeout)
101+
.as(Duration::toMillis)
102+
.to(customizeAbstractConnectors(factory, AbstractConnector::setIdleTimeout));
103103
map.from(properties::getAccesslog)
104104
.when(ServerProperties.Jetty.Accesslog::isEnabled)
105105
.to((accesslog) -> customizeAccessLog(factory, accesslog));
@@ -117,43 +117,63 @@ private boolean getOrDeduceUseForwardHeaders() {
117117
return this.serverProperties.getForwardHeadersStrategy().equals(ServerProperties.ForwardHeadersStrategy.NATIVE);
118118
}
119119

120-
private void customizeIdleTimeout(ConfigurableJettyWebServerFactory factory, Duration connectionTimeout) {
121-
factory.addServerCustomizers((server) -> {
122-
for (org.eclipse.jetty.server.Connector connector : server.getConnectors()) {
123-
if (connector instanceof AbstractConnector abstractConnector) {
124-
abstractConnector.setIdleTimeout(connectionTimeout.toMillis());
125-
}
126-
}
120+
private <T> Consumer<T> customizeHttpConfigurations(ConfigurableJettyWebServerFactory factory,
121+
BiConsumer<HttpConfiguration, T> action) {
122+
return customizeConnectionFactories(factory, HttpConfiguration.ConnectionFactory.class,
123+
(connectionFactory, value) -> action.accept(connectionFactory.getHttpConfiguration(), value));
124+
}
125+
126+
private <V, F> Consumer<V> customizeConnectionFactories(ConfigurableJettyWebServerFactory factory,
127+
Class<F> connectionFactoryType, BiConsumer<F, V> action) {
128+
return customizeConnectors(factory, Connector.class, (connector, value) -> {
129+
Stream<ConnectionFactory> connectionFactories = connector.getConnectionFactories().stream();
130+
forEach(connectionFactories, connectionFactoryType, action, value);
127131
});
128132
}
129133

130-
private void customizeServletContextHandler(ConfigurableJettyWebServerFactory factory, Consumer<ServletContextHandler> customFunc) {
131-
factory.addServerCustomizers(new JettyServerCustomizer() {
134+
private <V> Consumer<V> customizeAbstractConnectors(ConfigurableJettyWebServerFactory factory,
135+
BiConsumer<AbstractConnector, V> action) {
136+
return customizeConnectors(factory, AbstractConnector.class, action);
137+
}
132138

133-
@Override
134-
public void customize(Server server) {
135-
acceptCustomizeServletContextHandler(server.getHandlers());
136-
}
139+
private <V, C> Consumer<V> customizeConnectors(ConfigurableJettyWebServerFactory factory, Class<C> connectorType,
140+
BiConsumer<C, V> action) {
141+
return (value) -> factory.addServerCustomizers((server) -> {
142+
Stream<Connector> connectors = Arrays.stream(server.getConnectors());
143+
forEach(connectors, connectorType, action, value);
144+
});
145+
}
137146

138-
private void acceptCustomizeServletContextHandler(List<Handler> handlers) {
139-
for (Handler handler : handlers) {
140-
acceptCustomizeServletContextHandler(handler);
141-
}
142-
}
147+
private <V> Consumer<V> customizeServletContextHandler(ConfigurableJettyWebServerFactory factory,
148+
BiConsumer<ServletContextHandler, V> action) {
149+
return customizeHandlers(factory, ServletContextHandler.class, action);
150+
}
143151

144-
private void acceptCustomizeServletContextHandler(Handler handler) {
145-
if (handler instanceof ServletContextHandler contextHandler) {
146-
customFunc.accept(contextHandler);
147-
}
148-
else if (handler instanceof Handler.Wrapper wrapper) {
149-
acceptCustomizeServletContextHandler(wrapper.getHandler());
150-
}
151-
else if (handler instanceof Handler.Collection collection) {
152-
acceptCustomizeServletContextHandler(collection.getHandlers());
153-
}
152+
private <V, H> Consumer<V> customizeHandlers(ConfigurableJettyWebServerFactory factory, Class<H> handlerType,
153+
BiConsumer<H, V> action) {
154+
return (value) -> factory.addServerCustomizers((server) -> {
155+
List<Handler> handlers = server.getHandlers();
156+
forEachHandler(handlers, handlerType, action, value);
157+
});
158+
}
159+
160+
@SuppressWarnings("unchecked")
161+
private <V, H> void forEachHandler(List<Handler> handlers, Class<H> handlerType, BiConsumer<H, V> action, V value) {
162+
for (Handler handler : handlers) {
163+
if (handlerType.isInstance(handler)) {
164+
action.accept((H) handler, value);
154165
}
166+
if (handler instanceof Handler.Wrapper wrapper) {
167+
forEachHandler(wrapper.getHandlers(), handlerType, action, value);
168+
}
169+
if (handler instanceof Handler.Collection collection) {
170+
forEachHandler(collection.getHandlers(), handlerType, action, value);
171+
}
172+
}
173+
}
155174

156-
});
175+
private <T, V> void forEach(Stream<?> elements, Class<T> type, BiConsumer<T, V> action, V value) {
176+
elements.filter(type::isInstance).map(type::cast).forEach((element) -> action.accept(element, value));
157177
}
158178

159179
private void customizeAccessLog(ConfigurableJettyWebServerFactory factory,
@@ -181,61 +201,10 @@ private String getLogFormat(ServerProperties.Jetty.Accesslog properties) {
181201
if (properties.getCustomFormat() != null) {
182202
return properties.getCustomFormat();
183203
}
184-
else if (ServerProperties.Jetty.Accesslog.FORMAT.EXTENDED_NCSA.equals(properties.getFormat())) {
204+
if (ServerProperties.Jetty.Accesslog.FORMAT.EXTENDED_NCSA.equals(properties.getFormat())) {
185205
return CustomRequestLog.EXTENDED_NCSA_FORMAT;
186206
}
187207
return CustomRequestLog.NCSA_FORMAT;
188208
}
189209

190-
private static class MaxHttpRequestHeaderSizeCustomizer implements JettyServerCustomizer {
191-
192-
private final int maxRequestHeaderSize;
193-
194-
MaxHttpRequestHeaderSizeCustomizer(int maxRequestHeaderSize) {
195-
this.maxRequestHeaderSize = maxRequestHeaderSize;
196-
}
197-
198-
@Override
199-
public void customize(Server server) {
200-
Arrays.stream(server.getConnectors()).forEach(this::customize);
201-
}
202-
203-
private void customize(org.eclipse.jetty.server.Connector connector) {
204-
connector.getConnectionFactories().forEach(this::customize);
205-
}
206-
207-
private void customize(ConnectionFactory factory) {
208-
if (factory instanceof HttpConfiguration.ConnectionFactory) {
209-
((HttpConfiguration.ConnectionFactory) factory).getHttpConfiguration()
210-
.setRequestHeaderSize(this.maxRequestHeaderSize);
211-
}
212-
}
213-
214-
}
215-
216-
private static class MaxHttpResponseHeaderSizeCustomizer implements JettyServerCustomizer {
217-
218-
private final int maxResponseHeaderSize;
219-
220-
MaxHttpResponseHeaderSizeCustomizer(int maxResponseHeaderSize) {
221-
this.maxResponseHeaderSize = maxResponseHeaderSize;
222-
}
223-
224-
@Override
225-
public void customize(Server server) {
226-
Arrays.stream(server.getConnectors()).forEach(this::customize);
227-
}
228-
229-
private void customize(org.eclipse.jetty.server.Connector connector) {
230-
connector.getConnectionFactories().forEach(this::customize);
231-
}
232-
233-
private void customize(ConnectionFactory factory) {
234-
if (factory instanceof HttpConfiguration.ConnectionFactory httpConnectionFactory) {
235-
httpConnectionFactory.getHttpConfiguration().setResponseHeaderSize(this.maxResponseHeaderSize);
236-
}
237-
}
238-
239-
}
240-
241210
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ void jettyMaxFormKeysMatchesDefault() {
471471
JettyWebServer jetty = (JettyWebServer) jettyFactory.getWebServer();
472472
Server server = jetty.getServer();
473473
assertThat(this.properties.getJetty().getMaxFormKeys())
474-
.isEqualTo(((ServletContextHandler) server.getHandler()).getMaxFormKeys());
474+
.isEqualTo(((ServletContextHandler) server.getHandler()).getMaxFormKeys());
475475
}
476476

477477
@Test

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -329,23 +329,19 @@ void customIdleTimeout() {
329329
void customMaxFormKeys() {
330330
bind("server.jetty.max-form-keys=2048");
331331
JettyWebServer server = customizeAndGetServer();
332-
List<Integer> maxFormKeys = getMaxFormKeys(server);
332+
startAndStopToMakeInternalsAvailable(server);
333+
List<Integer> maxFormKeys = server.getServer()
334+
.getHandlers()
335+
.stream()
336+
.filter(ServletContextHandler.class::isInstance)
337+
.map(ServletContextHandler.class::cast)
338+
.map(ServletContextHandler::getMaxFormKeys)
339+
.toList();
333340
assertThat(maxFormKeys).containsOnly(2048);
334341
}
335342

336-
private List<Integer> getMaxFormKeys(JettyWebServer server) {
337-
server.start();
338-
server.stop();
339-
return server.getServer().getHandlers().stream()
340-
.filter(handler -> handler instanceof ServletContextHandler)
341-
.map(handler -> ((ServletContextHandler) handler).getMaxFormKeys())
342-
.toList();
343-
}
344-
345343
private List<Long> connectorsIdleTimeouts(JettyWebServer server) {
346-
// Start (and directly stop) server to have connectors available
347-
server.start();
348-
server.stop();
344+
startAndStopToMakeInternalsAvailable(server);
349345
return Arrays.stream(server.getServer().getConnectors())
350346
.filter((connector) -> connector instanceof AbstractConnector)
351347
.map(Connector::getIdleTimeout)
@@ -362,9 +358,7 @@ private List<Integer> getResponseHeaderSizes(JettyWebServer server) {
362358

363359
private List<Integer> getHeaderSizes(JettyWebServer server, Function<HttpConfiguration, Integer> provider) {
364360
List<Integer> requestHeaderSizes = new ArrayList<>();
365-
// Start (and directly stop) server to have connectors available
366-
server.start();
367-
server.stop();
361+
startAndStopToMakeInternalsAvailable(server);
368362
Connector[] connectors = server.getServer().getConnectors();
369363
for (Connector connector : connectors) {
370364
connector.getConnectionFactories()
@@ -379,6 +373,11 @@ private List<Integer> getHeaderSizes(JettyWebServer server, Function<HttpConfigu
379373
return requestHeaderSizes;
380374
}
381375

376+
private void startAndStopToMakeInternalsAvailable(JettyWebServer server) {
377+
server.start();
378+
server.stop();
379+
}
380+
382381
private BlockingQueue<?> getQueue(ThreadPool threadPool) {
383382
return ReflectionTestUtils.invokeMethod(threadPool, "getQueue");
384383
}

0 commit comments

Comments
 (0)