Skip to content

Commit b6d8e72

Browse files
committed
Merge branch '3.3.x' into 3.4.x
Closes gh-45870
2 parents 84e0db5 + d9e4b66 commit b6d8e72

File tree

4 files changed

+122
-2
lines changed

4 files changed

+122
-2
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,20 @@ public static class Tomcat {
413413
*/
414414
private DataSize maxHttpFormPostSize = DataSize.ofMegabytes(2);
415415

416+
/**
417+
* Maximum per-part header size permitted in a multipart/form-data request.
418+
* Requests that exceed this limit will be rejected. A value of less than 0 means
419+
* no limit.
420+
*/
421+
private DataSize maxPartHeaderSize = DataSize.ofBytes(512);
422+
423+
/**
424+
* Maximum total number of parts permitted in a multipart/form-data request.
425+
* Requests that exceed this limit will be rejected. A value of less than 0 means
426+
* no limit.
427+
*/
428+
private int maxPartCount = 10;
429+
416430
/**
417431
* Maximum amount of request body to swallow.
418432
*/
@@ -528,6 +542,22 @@ public void setMaxHttpFormPostSize(DataSize maxHttpFormPostSize) {
528542
this.maxHttpFormPostSize = maxHttpFormPostSize;
529543
}
530544

545+
public DataSize getMaxPartHeaderSize() {
546+
return this.maxPartHeaderSize;
547+
}
548+
549+
public void setMaxPartHeaderSize(DataSize maxPartHeaderSize) {
550+
this.maxPartHeaderSize = maxPartHeaderSize;
551+
}
552+
553+
public int getMaxPartCount() {
554+
return this.maxPartCount;
555+
}
556+
557+
public void setMaxPartCount(int maxPartCount) {
558+
this.maxPartCount = maxPartCount;
559+
}
560+
531561
public Accesslog getAccesslog() {
532562
return this.accesslog;
533563
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 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.
@@ -119,6 +119,10 @@ public void customize(ConfigurableTomcatWebServerFactory factory) {
119119
.asInt(DataSize::toBytes)
120120
.when((maxHttpFormPostSize) -> maxHttpFormPostSize != 0)
121121
.to((maxHttpFormPostSize) -> customizeMaxHttpFormPostSize(factory, maxHttpFormPostSize));
122+
map.from(properties::getMaxPartHeaderSize)
123+
.asInt(DataSize::toBytes)
124+
.to((maxPartHeaderSize) -> customizeMaxPartHeaderSize(factory, maxPartHeaderSize));
125+
map.from(properties::getMaxPartCount).to((maxPartCount) -> customizeMaxPartCount(factory, maxPartCount));
122126
map.from(properties::getAccesslog)
123127
.when(ServerProperties.Tomcat.Accesslog::isEnabled)
124128
.to((enabled) -> customizeAccessLog(factory));
@@ -292,6 +296,28 @@ private void customizeMaxHttpFormPostSize(ConfigurableTomcatWebServerFactory fac
292296
factory.addConnectorCustomizers((connector) -> connector.setMaxPostSize(maxHttpFormPostSize));
293297
}
294298

299+
private void customizeMaxPartCount(ConfigurableTomcatWebServerFactory factory, int maxPartCount) {
300+
factory.addConnectorCustomizers((connector) -> {
301+
try {
302+
connector.setMaxPartCount(maxPartCount);
303+
}
304+
catch (NoSuchMethodError ex) {
305+
// Tomcat < 10.1.42
306+
}
307+
});
308+
}
309+
310+
private void customizeMaxPartHeaderSize(ConfigurableTomcatWebServerFactory factory, int maxPartHeaderSize) {
311+
factory.addConnectorCustomizers((connector) -> {
312+
try {
313+
connector.setMaxPartHeaderSize(maxPartHeaderSize);
314+
}
315+
catch (NoSuchMethodError ex) {
316+
// Tomcat < 10.1.42
317+
}
318+
});
319+
}
320+
295321
private void customizeAccessLog(ConfigurableTomcatWebServerFactory factory) {
296322
ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat();
297323
AccessLogValve valve = new AccessLogValve();

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,18 @@ void testCustomizeTomcatMinSpareThreads() {
256256
assertThat(this.properties.getTomcat().getThreads().getMinSpare()).isEqualTo(10);
257257
}
258258

259+
@Test
260+
void customizeTomcatMaxPartCount() {
261+
bind("server.tomcat.max-part-count", "5");
262+
assertThat(this.properties.getTomcat().getMaxPartCount()).isEqualTo(5);
263+
}
264+
265+
@Test
266+
void customizeTomcatMaxPartHeaderSize() {
267+
bind("server.tomcat.max-part-header-size", "128");
268+
assertThat(this.properties.getTomcat().getMaxPartHeaderSize()).isEqualTo(DataSize.ofBytes(128));
269+
}
270+
259271
@Test
260272
void testCustomizeJettyAcceptors() {
261273
bind("server.jetty.threads.acceptors", "10");
@@ -395,6 +407,17 @@ void tomcatMaxHttpFormPostSizeMatchesConnectorDefault() {
395407
.isEqualTo(getDefaultConnector().getMaxPostSize());
396408
}
397409

410+
@Test
411+
void tomcatMaxPartCountMatchesConnectorDefault() {
412+
assertThat(this.properties.getTomcat().getMaxPartCount()).isEqualTo(getDefaultConnector().getMaxPartCount());
413+
}
414+
415+
@Test
416+
void tomcatMaxPartHeaderSizeMatchesConnectorDefault() {
417+
assertThat(this.properties.getTomcat().getMaxPartHeaderSize().toBytes())
418+
.isEqualTo(getDefaultConnector().getMaxPartHeaderSize());
419+
}
420+
398421
@Test
399422
void tomcatUriEncodingMatchesConnectorDefault() {
400423
assertThat(this.properties.getTomcat().getUriEncoding().name())

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

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
import org.springframework.boot.context.properties.bind.Bindable;
3838
import org.springframework.boot.context.properties.bind.Binder;
3939
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
40+
import org.springframework.boot.testsupport.classpath.ClassPathOverrides;
41+
import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories;
4042
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
4143
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
4244
import org.springframework.boot.web.server.WebServer;
@@ -45,6 +47,7 @@
4547
import org.springframework.util.unit.DataSize;
4648

4749
import static org.assertj.core.api.Assertions.assertThat;
50+
import static org.assertj.core.api.Assertions.assertThatNoException;
4851

4952
/**
5053
* Tests for {@link TomcatWebServerFactoryCustomizer}
@@ -60,6 +63,7 @@
6063
* @author Parviz Rozikov
6164
* @author Moritz Halbritter
6265
*/
66+
@DirtiesUrlFactories
6367
class TomcatWebServerFactoryCustomizerTests {
6468

6569
private MockEnvironment environment;
@@ -177,6 +181,37 @@ void customMaxHttpFormPostSize() {
177181
(server) -> assertThat(server.getTomcat().getConnector().getMaxPostSize()).isEqualTo(10000));
178182
}
179183

184+
@Test
185+
void defaultMaxPartCount() {
186+
customizeAndRunServer(
187+
(server) -> assertThat(server.getTomcat().getConnector().getMaxPartCount()).isEqualTo(10));
188+
}
189+
190+
@Test
191+
void customMaxPartCount() {
192+
bind("server.tomcat.max-part-count=5");
193+
customizeAndRunServer((server) -> assertThat(server.getTomcat().getConnector().getMaxPartCount()).isEqualTo(5));
194+
}
195+
196+
@Test
197+
void defaultMaxPartHeaderSize() {
198+
customizeAndRunServer(
199+
(server) -> assertThat(server.getTomcat().getConnector().getMaxPartHeaderSize()).isEqualTo(512));
200+
}
201+
202+
@Test
203+
void customMaxPartHeaderSize() {
204+
bind("server.tomcat.max-part-header-size=4KB");
205+
customizeAndRunServer(
206+
(server) -> assertThat(server.getTomcat().getConnector().getMaxPartHeaderSize()).isEqualTo(4096));
207+
}
208+
209+
@Test
210+
@ClassPathOverrides("org.apache.tomcat.embed:tomcat-embed-core:10.1.41")
211+
void customizerIsCompatibleWithTomcatVersionsWithoutMaxPartCountAndMaxPartHeaderSize() {
212+
assertThatNoException().isThrownBy(this::customizeAndRunServer);
213+
}
214+
180215
@Test
181216
void defaultMaxHttpRequestHeaderSize() {
182217
customizeAndRunServer((server) -> assertThat(
@@ -586,11 +621,17 @@ private void bind(String... inlinedProperties) {
586621
Bindable.ofInstance(this.serverProperties));
587622
}
588623

624+
private void customizeAndRunServer() {
625+
customizeAndRunServer(null);
626+
}
627+
589628
private void customizeAndRunServer(Consumer<TomcatWebServer> consumer) {
590629
TomcatWebServer server = customizeAndGetServer();
591630
server.start();
592631
try {
593-
consumer.accept(server);
632+
if (consumer != null) {
633+
consumer.accept(server);
634+
}
594635
}
595636
finally {
596637
server.stop();

0 commit comments

Comments
 (0)