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
This commit is contained in:
@@ -31,6 +31,7 @@ import org.springframework.boot.web.codec.CodecCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader;
|
||||
import org.springframework.http.codec.multipart.PartEventHttpMessageReader;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||
|
||||
@@ -61,11 +62,18 @@ public class ReactiveMultipartAutoConfiguration {
|
||||
map.from(multipartProperties::getMaxDiskUsagePerPart).asInt(DataSize::toBytes)
|
||||
.to(defaultPartHttpMessageReader::setMaxDiskUsagePerPart);
|
||||
map.from(multipartProperties::getMaxParts).to(defaultPartHttpMessageReader::setMaxParts);
|
||||
map.from(multipartProperties::getStreaming).to(defaultPartHttpMessageReader::setStreaming);
|
||||
map.from(multipartProperties::getFileStorageDirectory).as(Paths::get)
|
||||
.to((dir) -> configureFileStorageDirectory(defaultPartHttpMessageReader, dir));
|
||||
map.from(multipartProperties::getHeadersCharset).to(defaultPartHttpMessageReader::setHeadersCharset);
|
||||
}
|
||||
else if (codec instanceof PartEventHttpMessageReader partEventHttpMessageReader) {
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(multipartProperties::getMaxInMemorySize).asInt(DataSize::toBytes)
|
||||
.to(partEventHttpMessageReader::setMaxInMemorySize);
|
||||
map.from(multipartProperties::getMaxHeadersSize).asInt(DataSize::toBytes)
|
||||
.to(partEventHttpMessageReader::setMaxHeadersSize);
|
||||
map.from(multipartProperties::getHeadersCharset).to(partEventHttpMessageReader::setHeadersCharset);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
@@ -21,11 +21,13 @@ import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader;
|
||||
import org.springframework.http.codec.multipart.PartEventHttpMessageReader;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
|
||||
/**
|
||||
* {@link ConfigurationProperties Configuration properties} for configuring multipart
|
||||
* support in Spring Webflux. Used to configure the {@link DefaultPartHttpMessageReader}.
|
||||
* support in Spring Webflux. Used to configure the {@link DefaultPartHttpMessageReader}
|
||||
* and the {@link PartEventHttpMessageReader}.
|
||||
*
|
||||
* @author Chris Bono
|
||||
* @since 2.6.0
|
||||
@@ -57,12 +59,6 @@ public class ReactiveMultipartProperties {
|
||||
*/
|
||||
private Integer maxParts = -1;
|
||||
|
||||
/**
|
||||
* Whether to stream directly from the parsed input buffer stream without storing in
|
||||
* memory nor file. Default is non-streaming.
|
||||
*/
|
||||
private Boolean streaming = Boolean.FALSE;
|
||||
|
||||
/**
|
||||
* Directory used to store file parts larger than 'maxInMemorySize'. Default is a
|
||||
* directory named 'spring-multipart' created under the system temporary directory.
|
||||
@@ -107,14 +103,6 @@ public class ReactiveMultipartProperties {
|
||||
this.maxParts = maxParts;
|
||||
}
|
||||
|
||||
public Boolean getStreaming() {
|
||||
return this.streaming;
|
||||
}
|
||||
|
||||
public void setStreaming(Boolean streaming) {
|
||||
this.streaming = streaming;
|
||||
}
|
||||
|
||||
public String getFileStorageDirectory() {
|
||||
return this.fileStorageDirectory;
|
||||
}
|
||||
|
||||
@@ -3376,6 +3376,14 @@
|
||||
"name": "any"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "spring.webflux.multipart.streaming",
|
||||
"type": "java.lang.Boolean",
|
||||
"deprecation": {
|
||||
"reason": "Replaced by the PartEventHttpMessageReader and the PartEvent API.",
|
||||
"level": "error"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.boot.test.context.runner.ReactiveWebApplicationContex
|
||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||
import org.springframework.boot.web.codec.CodecCustomizer;
|
||||
import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader;
|
||||
import org.springframework.http.codec.multipart.PartEventHttpMessageReader;
|
||||
import org.springframework.http.codec.support.DefaultServerCodecConfigurer;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||
@@ -36,6 +37,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* Tests for {@link ReactiveMultipartAutoConfiguration}.
|
||||
*
|
||||
* @author Chris Bono
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class ReactiveMultipartAutoConfigurationTests {
|
||||
|
||||
@@ -56,16 +58,17 @@ class ReactiveMultipartAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConfigureMultipartProperties() {
|
||||
this.contextRunner.withPropertyValues("spring.webflux.multipart.streaming:true",
|
||||
"spring.webflux.multipart.max-in-memory-size=1GB", "spring.webflux.multipart.max-headers-size=16KB",
|
||||
"spring.webflux.multipart.max-disk-usage-per-part=100MB", "spring.webflux.multipart.max-parts=7",
|
||||
"spring.webflux.multipart.headers-charset:UTF_16").run((context) -> {
|
||||
void shouldConfigureMultipartPropertiesForDefaultReader() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.webflux.multipart.max-in-memory-size=1GB",
|
||||
"spring.webflux.multipart.max-headers-size=16KB",
|
||||
"spring.webflux.multipart.max-disk-usage-per-part=100MB",
|
||||
"spring.webflux.multipart.max-parts=7", "spring.webflux.multipart.headers-charset:UTF_16")
|
||||
.run((context) -> {
|
||||
CodecCustomizer customizer = context.getBean(CodecCustomizer.class);
|
||||
DefaultServerCodecConfigurer configurer = new DefaultServerCodecConfigurer();
|
||||
customizer.customize(configurer);
|
||||
DefaultPartHttpMessageReader partReader = getPartReader(configurer);
|
||||
assertThat(partReader).hasFieldOrPropertyWithValue("streaming", true);
|
||||
DefaultPartHttpMessageReader partReader = getDefaultPartReader(configurer);
|
||||
assertThat(partReader).hasFieldOrPropertyWithValue("maxParts", 7);
|
||||
assertThat(partReader).hasFieldOrPropertyWithValue("maxHeadersSize",
|
||||
Math.toIntExact(DataSize.ofKilobytes(16).toBytes()));
|
||||
@@ -77,10 +80,33 @@ class ReactiveMultipartAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
private DefaultPartHttpMessageReader getPartReader(DefaultServerCodecConfigurer codecConfigurer) {
|
||||
@Test
|
||||
void shouldConfigureMultipartPropertiesForPartEventReader() {
|
||||
this.contextRunner.withPropertyValues("spring.webflux.multipart.max-in-memory-size=1GB",
|
||||
"spring.webflux.multipart.max-headers-size=16KB", "spring.webflux.multipart.headers-charset:UTF_16")
|
||||
.run((context) -> {
|
||||
CodecCustomizer customizer = context.getBean(CodecCustomizer.class);
|
||||
DefaultServerCodecConfigurer configurer = new DefaultServerCodecConfigurer();
|
||||
customizer.customize(configurer);
|
||||
PartEventHttpMessageReader partReader = getPartEventReader(configurer);
|
||||
assertThat(partReader).hasFieldOrPropertyWithValue("maxHeadersSize",
|
||||
Math.toIntExact(DataSize.ofKilobytes(16).toBytes()));
|
||||
assertThat(partReader).hasFieldOrPropertyWithValue("headersCharset", StandardCharsets.UTF_16);
|
||||
assertThat(partReader).hasFieldOrPropertyWithValue("maxInMemorySize",
|
||||
Math.toIntExact(DataSize.ofGigabytes(1).toBytes()));
|
||||
});
|
||||
}
|
||||
|
||||
private DefaultPartHttpMessageReader getDefaultPartReader(DefaultServerCodecConfigurer codecConfigurer) {
|
||||
return codecConfigurer.getReaders().stream().filter(DefaultPartHttpMessageReader.class::isInstance)
|
||||
.map(DefaultPartHttpMessageReader.class::cast).findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("Could not find DefaultPartHttpMessageReader"));
|
||||
}
|
||||
|
||||
private PartEventHttpMessageReader getPartEventReader(DefaultServerCodecConfigurer codecConfigurer) {
|
||||
return codecConfigurer.getReaders().stream().filter(PartEventHttpMessageReader.class::isInstance)
|
||||
.map(PartEventHttpMessageReader.class::cast).findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("Could not find PartEventHttpMessageReader"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
@@ -33,8 +33,6 @@ class ReactiveMultipartPropertiesTests {
|
||||
void defaultValuesAreConsistent() {
|
||||
ReactiveMultipartProperties multipartProperties = new ReactiveMultipartProperties();
|
||||
DefaultPartHttpMessageReader defaultPartHttpMessageReader = new DefaultPartHttpMessageReader();
|
||||
assertThat(defaultPartHttpMessageReader).hasFieldOrPropertyWithValue("streaming",
|
||||
multipartProperties.getStreaming());
|
||||
assertThat(defaultPartHttpMessageReader).hasFieldOrPropertyWithValue("maxInMemorySize",
|
||||
(int) multipartProperties.getMaxInMemorySize().toBytes());
|
||||
assertThat(defaultPartHttpMessageReader).hasFieldOrPropertyWithValue("maxHeadersSize",
|
||||
|
||||
Reference in New Issue
Block a user