Skip to content

Commit

Permalink
Clean multipart up only when data is read
Browse files Browse the repository at this point in the history
This commit ensures that any storage used for multipart handling only
gets cleaned up if multipart data is actually retrieved via
ServerWebExchange::getMultipartData.

Closes spring-projectsgh-30590
  • Loading branch information
poutsma authored and mdeinum committed Jun 29, 2023
1 parent ff5cb6d commit 5b3ecb0
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 15 deletions.
Expand Up @@ -146,14 +146,7 @@ default <T> T getAttributeOrDefault(String name, T defaultValue) {
* @since 6.0.10
* @see Part#delete()
*/
default Mono<Void> cleanupMultipart() {
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();
}
Mono<Void> cleanupMultipart();

/**
* Return the {@link LocaleContext} using the configured
Expand Down
Expand Up @@ -324,28 +324,30 @@ private static class DelegatingServerWebExchange implements ServerWebExchange {

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();
}
Expand Down Expand Up @@ -398,7 +400,23 @@ public Mono<MultiValueMap<String, Part>> getMultipartData() {
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() {
Expand Down

0 comments on commit 5b3ecb0

Please sign in to comment.