Fix empty body writing in EncoderHttpMessageWriter
Prior to this commit, an bug introduced in SPR-16949 prevented `Mono.empty` bodies from being written to the response. This commit ensures that empty bodies still trigger the writing to the response and does not hang the processing of the exchange. Issue: SPR-17220
This commit is contained in:
parent
1dac0df38b
commit
280da61d5c
|
|
@ -48,6 +48,7 @@ import org.springframework.util.Assert;
|
|||
* @author Arjen Poutsma
|
||||
* @author Sebastien Deleuze
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Brian Clozel
|
||||
* @since 5.0
|
||||
* @param <T> the type of objects in the input stream
|
||||
*/
|
||||
|
|
@ -119,9 +120,10 @@ public class EncoderHttpMessageWriter<T> implements HttpMessageWriter<T> {
|
|||
HttpHeaders headers = message.getHeaders();
|
||||
if (headers.getContentLength() < 0 && !headers.containsKey(HttpHeaders.TRANSFER_ENCODING)) {
|
||||
return Mono.from(body)
|
||||
.flatMap(dataBuffer -> {
|
||||
headers.setContentLength(dataBuffer.readableByteCount());
|
||||
return message.writeWith(Mono.just(dataBuffer));
|
||||
.defaultIfEmpty(message.bufferFactory().wrap(new byte[0]))
|
||||
.flatMap(buffer -> {
|
||||
headers.setContentLength(buffer.readableByteCount());
|
||||
return message.writeWith(Mono.just(buffer));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.http.codec;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
|
@ -28,8 +29,12 @@ import org.mockito.Mock;
|
|||
import org.mockito.MockitoAnnotations;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
|
||||
import org.springframework.util.MimeType;
|
||||
|
|
@ -50,6 +55,7 @@ import static org.springframework.http.MediaType.TEXT_XML;
|
|||
/**
|
||||
* Unit tests for {@link EncoderHttpMessageWriter}.
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class EncoderHttpMessageWriterTests {
|
||||
|
||||
|
|
@ -75,13 +81,13 @@ public class EncoderHttpMessageWriterTests {
|
|||
|
||||
|
||||
@Test
|
||||
public void getWritableMediaTypes() throws Exception {
|
||||
public void getWritableMediaTypes() {
|
||||
HttpMessageWriter<?> writer = getWriter(MimeTypeUtils.TEXT_HTML, MimeTypeUtils.TEXT_XML);
|
||||
assertEquals(Arrays.asList(TEXT_HTML, TEXT_XML), writer.getWritableMediaTypes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canWrite() throws Exception {
|
||||
public void canWrite() {
|
||||
HttpMessageWriter<?> writer = getWriter(MimeTypeUtils.TEXT_HTML);
|
||||
when(this.encoder.canEncode(forClass(String.class), TEXT_HTML)).thenReturn(true);
|
||||
|
||||
|
|
@ -90,7 +96,7 @@ public class EncoderHttpMessageWriterTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void useNegotiatedMediaType() throws Exception {
|
||||
public void useNegotiatedMediaType() {
|
||||
HttpMessageWriter<String> writer = getWriter(MimeTypeUtils.ALL);
|
||||
writer.write(Mono.just("body"), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS);
|
||||
|
||||
|
|
@ -99,7 +105,7 @@ public class EncoderHttpMessageWriterTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void useDefaultMediaType() throws Exception {
|
||||
public void useDefaultMediaType() {
|
||||
testDefaultMediaType(null);
|
||||
testDefaultMediaType(new MediaType("text", "*"));
|
||||
testDefaultMediaType(new MediaType("*", "*"));
|
||||
|
|
@ -108,7 +114,6 @@ public class EncoderHttpMessageWriterTests {
|
|||
|
||||
private void testDefaultMediaType(MediaType negotiatedMediaType) {
|
||||
|
||||
this.response = new MockServerHttpResponse();
|
||||
this.mediaTypeCaptor = ArgumentCaptor.forClass(MediaType.class);
|
||||
|
||||
MimeType defaultContentType = MimeTypeUtils.TEXT_XML;
|
||||
|
|
@ -120,7 +125,7 @@ public class EncoderHttpMessageWriterTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void useDefaultMediaTypeCharset() throws Exception {
|
||||
public void useDefaultMediaTypeCharset() {
|
||||
HttpMessageWriter<String> writer = getWriter(TEXT_PLAIN_UTF_8, TEXT_HTML);
|
||||
writer.write(Mono.just("body"), forClass(String.class), TEXT_HTML, response, NO_HINTS);
|
||||
|
||||
|
|
@ -129,7 +134,7 @@ public class EncoderHttpMessageWriterTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void useNegotiatedMediaTypeCharset() throws Exception {
|
||||
public void useNegotiatedMediaTypeCharset() {
|
||||
|
||||
MediaType negotiatedMediaType = new MediaType("text", "html", ISO_8859_1);
|
||||
|
||||
|
|
@ -141,7 +146,7 @@ public class EncoderHttpMessageWriterTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void useHttpOutputMessageMediaType() throws Exception {
|
||||
public void useHttpOutputMessageMediaType() {
|
||||
|
||||
MediaType outputMessageMediaType = MediaType.TEXT_HTML;
|
||||
this.response.getHeaders().setContentType(outputMessageMediaType);
|
||||
|
|
@ -153,11 +158,35 @@ public class EncoderHttpMessageWriterTests {
|
|||
assertEquals(outputMessageMediaType, this.mediaTypeCaptor.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setContentLengthForMonoBody() {
|
||||
|
||||
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
|
||||
DataBuffer buffer = factory.wrap("body".getBytes(StandardCharsets.UTF_8));
|
||||
HttpMessageWriter<String> writer = getWriter(Flux.just(buffer), MimeTypeUtils.TEXT_PLAIN);
|
||||
|
||||
writer.write(Mono.just("body"), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS).block();
|
||||
|
||||
assertEquals(4, this.response.getHeaders().getContentLength());
|
||||
}
|
||||
|
||||
@Test // SPR-17220
|
||||
public void emptyBodyWritten() {
|
||||
HttpMessageWriter<String> writer = getWriter(MimeTypeUtils.TEXT_PLAIN);
|
||||
writer.write(Mono.empty(), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS).block();
|
||||
StepVerifier.create(this.response.getBody()).expectNextCount(1).verifyComplete();
|
||||
assertEquals(0, this.response.getHeaders().getContentLength());
|
||||
}
|
||||
|
||||
|
||||
private HttpMessageWriter<String> getWriter(MimeType... mimeTypes) {
|
||||
return getWriter(Flux.empty(), mimeTypes);
|
||||
}
|
||||
|
||||
private HttpMessageWriter<String> getWriter(Flux<DataBuffer> encodedStream, MimeType... mimeTypes) {
|
||||
List<MimeType> typeList = Arrays.asList(mimeTypes);
|
||||
when(this.encoder.getEncodableMimeTypes()).thenReturn(typeList);
|
||||
when(this.encoder.encode(any(), any(), any(), this.mediaTypeCaptor.capture(), any())).thenReturn(Flux.empty());
|
||||
when(this.encoder.encode(any(), any(), any(), this.mediaTypeCaptor.capture(), any())).thenReturn(encodedStream);
|
||||
return new EncoderHttpMessageWriter<>(this.encoder);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue