Browse Source

Fix duplicate header writing in ResponseBodyEmitterReturnValueHandler

Closes gh-36357
pull/36359/head
rstoyanchev 4 weeks ago
parent
commit
d4b2a493f9
  1. 7
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java
  2. 24
      spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandlerTests.java

7
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java

@ -214,12 +214,7 @@ public class ResponseBodyEmitterReturnValueHandler implements HandlerMethodRetur @@ -214,12 +214,7 @@ public class ResponseBodyEmitterReturnValueHandler implements HandlerMethodRetur
else {
emitter = this.reactiveHandler.handleValue(returnValue, returnType, contentType, mavContainer, webRequest);
if (emitter == null) {
// We're not streaming; write headers without committing response
outputMessage.getHeaders().forEach((headerName, headerValues) -> {
for (String headerValue : headerValues) {
response.addHeader(headerName, headerValue);
}
});
// reactive but not streaming, e.g. Mono or aggregated Flux
return;
}
}

24
spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandlerTests.java

@ -30,6 +30,7 @@ import org.junit.jupiter.api.BeforeEach; @@ -30,6 +30,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Sinks;
import reactor.core.scheduler.Schedulers;
@ -359,6 +360,27 @@ class ResponseBodyEmitterReturnValueHandlerTests { @@ -359,6 +360,27 @@ class ResponseBodyEmitterReturnValueHandlerTests {
assertThat(this.response.getContentAsString()).isEqualTo("data:foo\n\ndata:bar\n\n");
}
@Test // gh-36357
void responseEntityMono() throws Exception {
ResponseEntity<Publisher<?>> entity = ResponseEntity.ok()
.contentType(MediaType.TEXT_PLAIN)
.header("X-Custom", "value")
.body(Mono.just("foo"));
ResolvableType bodyType = forClassWithGenerics(Mono.class, String.class);
MethodParameter type = on(TestController.class).resolveReturnType(ResponseEntity.class, bodyType);
this.handler.handleReturnValue(entity, type, this.mavContainer, this.webRequest);
assertThat(this.response.getStatus()).isEqualTo(200);
assertThat(this.response.getHeaders("Content-Type")).containsExactly("text/plain");
assertThat(this.response.getHeaders("X-Custom")).containsExactly("value");
WebAsyncManager manager = WebAsyncUtils.getAsyncManager(request);
assertThat(manager.isConcurrentHandlingStarted()).isTrue();
assertThat(manager.getConcurrentResult()).isEqualTo("foo");
}
@SuppressWarnings({"unused", "ConstantConditions"})
private static class TestController {
@ -385,6 +407,8 @@ class ResponseBodyEmitterReturnValueHandlerTests { @@ -385,6 +407,8 @@ class ResponseBodyEmitterReturnValueHandlerTests {
private ResponseEntity<Publisher<?>> h11() { return null; }
private ResponseEntity<Mono<String>> h12() { return null; }
}

Loading…
Cancel
Save