diff --git a/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java index 239ddf0a69f9..82896bb24077 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java +++ b/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java @@ -16,20 +16,15 @@ package org.springframework.http.codec; -import java.util.List; -import java.util.Map; - import org.apache.commons.logging.Log; import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - import org.springframework.core.ResolvableType; import org.springframework.core.codec.AbstractEncoder; import org.springframework.core.codec.Encoder; import org.springframework.core.codec.Hints; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpLogging; import org.springframework.http.MediaType; import org.springframework.http.ReactiveHttpOutputMessage; @@ -38,6 +33,11 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.List; +import java.util.Map; /** * {@code HttpMessageWriter} that wraps and delegates to an {@link Encoder}. @@ -127,6 +127,7 @@ public Mono write(Publisher inputStream, ResolvableType eleme return body .singleOrEmpty() .switchIfEmpty(Mono.defer(() -> { + message.getHeaders().remove(HttpHeaders.CONTENT_TYPE); message.getHeaders().setContentLength(0); return message.setComplete().then(Mono.empty()); })) diff --git a/spring-web/src/test/java/org/springframework/http/codec/EncoderHttpMessageWriterTests.java b/spring-web/src/test/java/org/springframework/http/codec/EncoderHttpMessageWriterTests.java index eff738c3c9f0..c1b65fc465fa 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/EncoderHttpMessageWriterTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/EncoderHttpMessageWriterTests.java @@ -16,31 +16,36 @@ package org.springframework.http.codec; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; - import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - +import org.springframework.core.ResolvableType; import org.springframework.core.codec.CharSequenceEncoder; +import org.springframework.core.codec.Encoder; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DefaultDataBufferFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; +import org.springframework.http.ReactiveHttpOutputMessage; import org.springframework.util.MimeType; import org.springframework.util.MimeTypeUtils; import org.springframework.util.ReflectionUtils; +import org.springframework.web.testfixture.http.client.reactive.MockClientHttpRequest; import org.springframework.web.testfixture.http.server.reactive.MockServerHttpResponse; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; import static java.nio.charset.StandardCharsets.ISO_8859_1; import static java.nio.charset.StandardCharsets.UTF_8; @@ -199,6 +204,35 @@ void isStreamingMediaType() throws InvocationTargetException, IllegalAccessExcep assertThat((Boolean) method.invoke(writer, TEXT_HTML)).isFalse(); } + @Test + public void ifBodyPublisherEmpty_noContentTypeHeader() { + Encoder encoder = CharSequenceEncoder.textPlainOnly(); + EncoderHttpMessageWriter writer = new EncoderHttpMessageWriter<>(encoder); + ReactiveHttpOutputMessage outputMessage = new MockClientHttpRequest(HttpMethod.POST, "/"); + Mono writerMono = writer.write(Mono.empty(), ResolvableType.forClass(String.class), + null, outputMessage, NO_HINTS); + + StepVerifier.create(writerMono) + .verifyComplete(); + assertThat(outputMessage.getHeaders()).doesNotContainKey(HttpHeaders.CONTENT_TYPE); + } + + @Test + public void ifBodyPublisherEmpty_contentLengthZero() { + Encoder encoder = CharSequenceEncoder.textPlainOnly(); + EncoderHttpMessageWriter writer = new EncoderHttpMessageWriter<>(encoder); + ReactiveHttpOutputMessage outputMessage = new MockClientHttpRequest(HttpMethod.POST, "/"); + Mono writerMono = writer.write(Mono.empty(), ResolvableType.forClass(String.class), + null, outputMessage, NO_HINTS); + + StepVerifier.create(writerMono) + .verifyComplete(); + List contentLengthValues = outputMessage.getHeaders().get(HttpHeaders.CONTENT_LENGTH); + assertThat(contentLengthValues).hasSize(1); + int contentLength = Integer.parseInt(contentLengthValues.get(0)); + assertThat(contentLength).isEqualTo(0); + } + private void configureEncoder(MimeType... mimeTypes) { configureEncoder(Flux.empty(), mimeTypes); }