Browse Source

Remove Content-Type when body is empty

Closes gh-32622
See gh-32620
pull/32696/head
Sergey Zolotaryov 2 years ago committed by Arjen Poutsma
parent
commit
3f8a10c19f
  1. 13
      spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java
  2. 58
      spring-web/src/test/java/org/springframework/http/codec/EncoderHttpMessageWriterTests.java

13
spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java

@ -16,20 +16,15 @@
package org.springframework.http.codec; package org.springframework.http.codec;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.core.codec.AbstractEncoder; import org.springframework.core.codec.AbstractEncoder;
import org.springframework.core.codec.Encoder; import org.springframework.core.codec.Encoder;
import org.springframework.core.codec.Hints; import org.springframework.core.codec.Hints;
import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpLogging; import org.springframework.http.HttpLogging;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpOutputMessage; import org.springframework.http.ReactiveHttpOutputMessage;
@ -38,6 +33,11 @@ import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils; 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}. * {@code HttpMessageWriter} that wraps and delegates to an {@link Encoder}.
@ -127,6 +127,7 @@ public class EncoderHttpMessageWriter<T> implements HttpMessageWriter<T> {
return body return body
.singleOrEmpty() .singleOrEmpty()
.switchIfEmpty(Mono.defer(() -> { .switchIfEmpty(Mono.defer(() -> {
message.getHeaders().remove(HttpHeaders.CONTENT_TYPE);
message.getHeaders().setContentLength(0); message.getHeaders().setContentLength(0);
return message.setComplete().then(Mono.empty()); return message.setComplete().then(Mono.empty());
})) }))

58
spring-web/src/test/java/org/springframework/http/codec/EncoderHttpMessageWriterTests.java

@ -16,31 +16,36 @@
package org.springframework.http.codec; 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.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness; import org.mockito.quality.Strictness;
import reactor.core.publisher.Flux; import org.springframework.core.ResolvableType;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.core.codec.CharSequenceEncoder; 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.DataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory; 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.MediaType;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.util.MimeType; import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils; import org.springframework.util.MimeTypeUtils;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import org.springframework.web.testfixture.http.client.reactive.MockClientHttpRequest;
import org.springframework.web.testfixture.http.server.reactive.MockServerHttpResponse; 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.ISO_8859_1;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
@ -199,6 +204,35 @@ class EncoderHttpMessageWriterTests {
assertThat((Boolean) method.invoke(writer, TEXT_HTML)).isFalse(); assertThat((Boolean) method.invoke(writer, TEXT_HTML)).isFalse();
} }
@Test
public void ifBodyPublisherEmpty_noContentTypeHeader() {
Encoder<CharSequence> encoder = CharSequenceEncoder.textPlainOnly();
EncoderHttpMessageWriter<CharSequence> writer = new EncoderHttpMessageWriter<>(encoder);
ReactiveHttpOutputMessage outputMessage = new MockClientHttpRequest(HttpMethod.POST, "/");
Mono<Void> 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<CharSequence> encoder = CharSequenceEncoder.textPlainOnly();
EncoderHttpMessageWriter<CharSequence> writer = new EncoderHttpMessageWriter<>(encoder);
ReactiveHttpOutputMessage outputMessage = new MockClientHttpRequest(HttpMethod.POST, "/");
Mono<Void> writerMono = writer.write(Mono.empty(), ResolvableType.forClass(String.class),
null, outputMessage, NO_HINTS);
StepVerifier.create(writerMono)
.verifyComplete();
List<String> 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) { private void configureEncoder(MimeType... mimeTypes) {
configureEncoder(Flux.empty(), mimeTypes); configureEncoder(Flux.empty(), mimeTypes);
} }

Loading…
Cancel
Save