Skip to content

Commit d870474

Browse files
committed
Remove spring.webflux.multipart.streaming property
As of spring-projects/spring-framework#29293, the streaming mode on the `DefaultPartHttpMessageReader` is deprecated as hard limitations have been found with the design and won't be fixed. Instead, developers should use the `PartEvent` API and the `PartEventHttpMessageReader` (which is configured by default with the codecs). This commit removes the `spring.webflux.multipart.streaming` property and applies all `spring.webflux.multipart.*` properties that are applicable to `PartEventHttpMessageReader`. Closes gh-32658
1 parent 4e46f86 commit d870474

File tree

5 files changed

+56
-28
lines changed

5 files changed

+56
-28
lines changed

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

+9-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.context.annotation.Bean;
3232
import org.springframework.core.annotation.Order;
3333
import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader;
34+
import org.springframework.http.codec.multipart.PartEventHttpMessageReader;
3435
import org.springframework.util.unit.DataSize;
3536
import org.springframework.web.reactive.config.WebFluxConfigurer;
3637

@@ -61,11 +62,18 @@ CodecCustomizer defaultPartHttpMessageReaderCustomizer(ReactiveMultipartProperti
6162
map.from(multipartProperties::getMaxDiskUsagePerPart).asInt(DataSize::toBytes)
6263
.to(defaultPartHttpMessageReader::setMaxDiskUsagePerPart);
6364
map.from(multipartProperties::getMaxParts).to(defaultPartHttpMessageReader::setMaxParts);
64-
map.from(multipartProperties::getStreaming).to(defaultPartHttpMessageReader::setStreaming);
6565
map.from(multipartProperties::getFileStorageDirectory).as(Paths::get)
6666
.to((dir) -> configureFileStorageDirectory(defaultPartHttpMessageReader, dir));
6767
map.from(multipartProperties::getHeadersCharset).to(defaultPartHttpMessageReader::setHeadersCharset);
6868
}
69+
else if (codec instanceof PartEventHttpMessageReader partEventHttpMessageReader) {
70+
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
71+
map.from(multipartProperties::getMaxInMemorySize).asInt(DataSize::toBytes)
72+
.to(partEventHttpMessageReader::setMaxInMemorySize);
73+
map.from(multipartProperties::getMaxHeadersSize).asInt(DataSize::toBytes)
74+
.to(partEventHttpMessageReader::setMaxHeadersSize);
75+
map.from(multipartProperties::getHeadersCharset).to(partEventHttpMessageReader::setHeadersCharset);
76+
}
6977
});
7078
}
7179

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

+4-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 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.
@@ -21,11 +21,13 @@
2121

2222
import org.springframework.boot.context.properties.ConfigurationProperties;
2323
import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader;
24+
import org.springframework.http.codec.multipart.PartEventHttpMessageReader;
2425
import org.springframework.util.unit.DataSize;
2526

2627
/**
2728
* {@link ConfigurationProperties Configuration properties} for configuring multipart
28-
* support in Spring Webflux. Used to configure the {@link DefaultPartHttpMessageReader}.
29+
* support in Spring Webflux. Used to configure the {@link DefaultPartHttpMessageReader}
30+
* and the {@link PartEventHttpMessageReader}.
2931
*
3032
* @author Chris Bono
3133
* @since 2.6.0
@@ -57,12 +59,6 @@ public class ReactiveMultipartProperties {
5759
*/
5860
private Integer maxParts = -1;
5961

60-
/**
61-
* Whether to stream directly from the parsed input buffer stream without storing in
62-
* memory nor file. Default is non-streaming.
63-
*/
64-
private Boolean streaming = Boolean.FALSE;
65-
6662
/**
6763
* Directory used to store file parts larger than 'maxInMemorySize'. Default is a
6864
* directory named 'spring-multipart' created under the system temporary directory.
@@ -107,14 +103,6 @@ public void setMaxParts(Integer maxParts) {
107103
this.maxParts = maxParts;
108104
}
109105

110-
public Boolean getStreaming() {
111-
return this.streaming;
112-
}
113-
114-
public void setStreaming(Boolean streaming) {
115-
this.streaming = streaming;
116-
}
117-
118106
public String getFileStorageDirectory() {
119107
return this.fileStorageDirectory;
120108
}

spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json

+8
Original file line numberDiff line numberDiff line change
@@ -3376,6 +3376,14 @@
33763376
"name": "any"
33773377
}
33783378
]
3379+
},
3380+
{
3381+
"name": "spring.webflux.multipart.streaming",
3382+
"type": "java.lang.Boolean",
3383+
"deprecation": {
3384+
"reason": "Replaced by the PartEventHttpMessageReader and the PartEvent API.",
3385+
"level": "error"
3386+
}
33793387
}
33803388
]
33813389
}

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

+34-8
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
2727
import org.springframework.boot.web.codec.CodecCustomizer;
2828
import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader;
29+
import org.springframework.http.codec.multipart.PartEventHttpMessageReader;
2930
import org.springframework.http.codec.support.DefaultServerCodecConfigurer;
3031
import org.springframework.util.unit.DataSize;
3132
import org.springframework.web.reactive.config.WebFluxConfigurer;
@@ -36,6 +37,7 @@
3637
* Tests for {@link ReactiveMultipartAutoConfiguration}.
3738
*
3839
* @author Chris Bono
40+
* @author Brian Clozel
3941
*/
4042
class ReactiveMultipartAutoConfigurationTests {
4143

@@ -56,16 +58,17 @@ void shouldNotProvideCustomizerWhenWebFluxNotAvailable() {
5658
}
5759

5860
@Test
59-
void shouldConfigureMultipartProperties() {
60-
this.contextRunner.withPropertyValues("spring.webflux.multipart.streaming:true",
61-
"spring.webflux.multipart.max-in-memory-size=1GB", "spring.webflux.multipart.max-headers-size=16KB",
62-
"spring.webflux.multipart.max-disk-usage-per-part=100MB", "spring.webflux.multipart.max-parts=7",
63-
"spring.webflux.multipart.headers-charset:UTF_16").run((context) -> {
61+
void shouldConfigureMultipartPropertiesForDefaultReader() {
62+
this.contextRunner
63+
.withPropertyValues("spring.webflux.multipart.max-in-memory-size=1GB",
64+
"spring.webflux.multipart.max-headers-size=16KB",
65+
"spring.webflux.multipart.max-disk-usage-per-part=100MB",
66+
"spring.webflux.multipart.max-parts=7", "spring.webflux.multipart.headers-charset:UTF_16")
67+
.run((context) -> {
6468
CodecCustomizer customizer = context.getBean(CodecCustomizer.class);
6569
DefaultServerCodecConfigurer configurer = new DefaultServerCodecConfigurer();
6670
customizer.customize(configurer);
67-
DefaultPartHttpMessageReader partReader = getPartReader(configurer);
68-
assertThat(partReader).hasFieldOrPropertyWithValue("streaming", true);
71+
DefaultPartHttpMessageReader partReader = getDefaultPartReader(configurer);
6972
assertThat(partReader).hasFieldOrPropertyWithValue("maxParts", 7);
7073
assertThat(partReader).hasFieldOrPropertyWithValue("maxHeadersSize",
7174
Math.toIntExact(DataSize.ofKilobytes(16).toBytes()));
@@ -77,10 +80,33 @@ void shouldConfigureMultipartProperties() {
7780
});
7881
}
7982

80-
private DefaultPartHttpMessageReader getPartReader(DefaultServerCodecConfigurer codecConfigurer) {
83+
@Test
84+
void shouldConfigureMultipartPropertiesForPartEventReader() {
85+
this.contextRunner.withPropertyValues("spring.webflux.multipart.max-in-memory-size=1GB",
86+
"spring.webflux.multipart.max-headers-size=16KB", "spring.webflux.multipart.headers-charset:UTF_16")
87+
.run((context) -> {
88+
CodecCustomizer customizer = context.getBean(CodecCustomizer.class);
89+
DefaultServerCodecConfigurer configurer = new DefaultServerCodecConfigurer();
90+
customizer.customize(configurer);
91+
PartEventHttpMessageReader partReader = getPartEventReader(configurer);
92+
assertThat(partReader).hasFieldOrPropertyWithValue("maxHeadersSize",
93+
Math.toIntExact(DataSize.ofKilobytes(16).toBytes()));
94+
assertThat(partReader).hasFieldOrPropertyWithValue("headersCharset", StandardCharsets.UTF_16);
95+
assertThat(partReader).hasFieldOrPropertyWithValue("maxInMemorySize",
96+
Math.toIntExact(DataSize.ofGigabytes(1).toBytes()));
97+
});
98+
}
99+
100+
private DefaultPartHttpMessageReader getDefaultPartReader(DefaultServerCodecConfigurer codecConfigurer) {
81101
return codecConfigurer.getReaders().stream().filter(DefaultPartHttpMessageReader.class::isInstance)
82102
.map(DefaultPartHttpMessageReader.class::cast).findFirst()
83103
.orElseThrow(() -> new IllegalStateException("Could not find DefaultPartHttpMessageReader"));
84104
}
85105

106+
private PartEventHttpMessageReader getPartEventReader(DefaultServerCodecConfigurer codecConfigurer) {
107+
return codecConfigurer.getReaders().stream().filter(PartEventHttpMessageReader.class::isInstance)
108+
.map(PartEventHttpMessageReader.class::cast).findFirst()
109+
.orElseThrow(() -> new IllegalStateException("Could not find PartEventHttpMessageReader"));
110+
}
111+
86112
}

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

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 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.
@@ -33,8 +33,6 @@ class ReactiveMultipartPropertiesTests {
3333
void defaultValuesAreConsistent() {
3434
ReactiveMultipartProperties multipartProperties = new ReactiveMultipartProperties();
3535
DefaultPartHttpMessageReader defaultPartHttpMessageReader = new DefaultPartHttpMessageReader();
36-
assertThat(defaultPartHttpMessageReader).hasFieldOrPropertyWithValue("streaming",
37-
multipartProperties.getStreaming());
3836
assertThat(defaultPartHttpMessageReader).hasFieldOrPropertyWithValue("maxInMemorySize",
3937
(int) multipartProperties.getMaxInMemorySize().toBytes());
4038
assertThat(defaultPartHttpMessageReader).hasFieldOrPropertyWithValue("maxHeadersSize",

0 commit comments

Comments
 (0)