WebClient writes Content-Length for Mono bodies

In SPR-16892, the `EncoderHttpMessageWriter` has been improved to write
`"Content-Length"` HTTP response headers if the response body is of type
`Mono` (i.e. the actual content length is easily accessible without
buffering a possibly large response body). That change was relying on
the fact that the server side is using a `ChannelSendOperator` to delay
the writing of the body until the first signal is received.

This strategy is not effective on the client side, since no such channel
operator is used for `WebClient`. This commit improves
`EncoderHttpMessageWriter` and delays, for `Mono` HTTP message bodies
only, the writing of the body so that we can write the
`"Content-Length"` header information once we've got the body resolved.

Issue: SPR-16949
This commit is contained in:
Brian Clozel
2018-06-19 11:31:04 +02:00
parent a774305cfc
commit 4a26f93a0d
4 changed files with 16 additions and 11 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -118,12 +118,12 @@ public abstract class AbstractClientHttpRequest implements ClientHttpRequest {
return Mono.empty();
}
this.commitActions.add(() -> {
applyHeaders();
applyCookies();
this.state.set(State.COMMITTED);
return Mono.empty();
});
this.commitActions.add(() ->
Mono.fromRunnable(() -> {
applyHeaders();
applyCookies();
this.state.set(State.COMMITTED);
}));
if (writeAction != null) {
this.commitActions.add(writeAction);
@@ -132,7 +132,7 @@ public abstract class AbstractClientHttpRequest implements ClientHttpRequest {
List<? extends Publisher<Void>> actions = this.commitActions.stream()
.map(Supplier::get).collect(Collectors.toList());
return Mono.fromDirect(Flux.concat(actions));
return Flux.concat(actions).then();
}

View File

@@ -103,11 +103,14 @@ public class EncoderHttpMessageWriter<T> implements HttpMessageWriter<T> {
Flux<DataBuffer> body = this.encoder.encode(
inputStream, message.bufferFactory(), elementType, contentType, hints);
// Response is not committed until the first signal...
if (inputStream instanceof Mono) {
HttpHeaders headers = message.getHeaders();
if (headers.getContentLength() < 0 && !headers.containsKey(HttpHeaders.TRANSFER_ENCODING)) {
body = body.doOnNext(data -> headers.setContentLength(data.readableByteCount()));
return Mono.from(body)
.flatMap(dataBuffer -> {
headers.setContentLength(dataBuffer.readableByteCount());
return message.writeWith(Mono.just(dataBuffer));
});
}
}