Clean multipart up only when data is read

This commit ensures that any storage used for multipart handling only
gets cleaned up if multipart data is actually retrieved via
ServerWebExchange::getMultipartData.

Closes gh-30590
This commit is contained in:
Arjen Poutsma
2023-06-08 14:23:47 +02:00
parent 2c8d1b7bff
commit c1fe57135e
5 changed files with 64 additions and 38 deletions

View File

@@ -324,28 +324,30 @@ class DefaultServerRequestBuilder implements ServerRequest.Builder {
private final Mono<MultiValueMap<String, Part>> multipartDataMono;
private volatile boolean multipartRead = false;
DelegatingServerWebExchange(ServerHttpRequest request, Map<String, Object> attributes,
ServerWebExchange delegate, List<HttpMessageReader<?>> messageReaders) {
this.request = request;
this.attributes = attributes;
this.delegate = delegate;
this.formDataMono = initFormData(request, messageReaders);
this.formDataMono = initFormData(messageReaders);
this.multipartDataMono = initMultipartData(request, messageReaders);
}
@SuppressWarnings("unchecked")
private static Mono<MultiValueMap<String, String>> initFormData(ServerHttpRequest request,
List<HttpMessageReader<?>> readers) {
private Mono<MultiValueMap<String, String>> initFormData(List<HttpMessageReader<?>> readers) {
try {
MediaType contentType = request.getHeaders().getContentType();
MediaType contentType = this.request.getHeaders().getContentType();
if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)) {
return ((HttpMessageReader<MultiValueMap<String, String>>) readers.stream()
.filter(reader -> reader.canRead(FORM_DATA_TYPE, MediaType.APPLICATION_FORM_URLENCODED))
.findFirst()
.orElseThrow(() -> new IllegalStateException("No form data HttpMessageReader.")))
.readMono(FORM_DATA_TYPE, request, Hints.none())
.readMono(FORM_DATA_TYPE, this.request, Hints.none())
.doOnNext(ignored -> this.multipartRead = true)
.switchIfEmpty(EMPTY_FORM_DATA)
.cache();
}
@@ -398,7 +400,23 @@ class DefaultServerRequestBuilder implements ServerRequest.Builder {
return this.multipartDataMono;
}
// Delegating methods
@Override
public Mono<Void> cleanupMultipart() {
if (this.multipartRead) {
return getMultipartData()
.onErrorResume(t -> Mono.empty()) // ignore errors reading multipart data
.flatMapIterable(Map::values)
.flatMapIterable(Function.identity())
.flatMap(part -> part.delete()
.onErrorResume(ex -> Mono.empty()))
.then();
}
else {
return Mono.empty();
}
}
// Delegating methods
@Override
public ServerHttpResponse getResponse() {