From 7e95cd8b4ee8ced753043495534e3d55c4892718 Mon Sep 17 00:00:00 2001 From: Lifu Zhou Date: Mon, 30 May 2016 14:38:59 +0800 Subject: [PATCH 1/3] Flush headers after null StreamingResponseBody Issue: SPR-14315 --- .../StreamingResponseBodyReturnValueHandler.java | 2 ++ ...treamingResponseBodyReturnValueHandlerTests.java | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBodyReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBodyReturnValueHandler.java index 63c37bea4c..fd48719af1 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBodyReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBodyReturnValueHandler.java @@ -74,6 +74,8 @@ public class StreamingResponseBodyReturnValueHandler implements HandlerMethodRet returnValue = responseEntity.getBody(); if (returnValue == null) { mavContainer.setRequestHandled(true); + // Ensure headers are flushed + outputMessage.flush(); return; } } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBodyReturnValueHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBodyReturnValueHandlerTests.java index 7be780de4f..7d5ce1f1ff 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBodyReturnValueHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBodyReturnValueHandlerTests.java @@ -29,6 +29,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.core.MethodParameter; +import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockHttpServletResponse; @@ -58,6 +59,8 @@ public class StreamingResponseBodyReturnValueHandlerTests { private MockHttpServletResponse response; + private HttpHeaders headers = new HttpHeaders(); + @Before public void setUp() throws Exception { @@ -69,6 +72,8 @@ public class StreamingResponseBodyReturnValueHandlerTests { this.response = new MockHttpServletResponse(); this.webRequest = new ServletWebRequest(this.request, this.response); + this.headers.add("foo", "bar"); + AsyncWebRequest asyncWebRequest = new StandardServletAsyncWebRequest(this.request, this.response); WebAsyncUtils.getAsyncManager(this.webRequest).setAsyncWebRequest(asyncWebRequest); this.request.setAsyncSupported(true); @@ -140,6 +145,14 @@ public class StreamingResponseBodyReturnValueHandlerTests { assertEquals(204, this.response.getStatus()); } + @Test + public void responseEntityWithHeadersAndNoContent() throws Exception { + MethodParameter returnType = returnType(TestController.class, "handleResponseEntity"); + ResponseEntity emitter = ResponseEntity.noContent().headers(headers).build(); + this.handler.handleReturnValue(emitter, returnType, this.mavContainer, this.webRequest); + + assertEquals(this.response.getHeaders("foo"), this.headers.get("foo")); + } private MethodParameter returnType(Class clazz, String methodName) throws NoSuchMethodException { Method method = clazz.getDeclaredMethod(methodName); From 431a50823f13955f3f7cf82be8d57fffbecd8221 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 31 May 2016 09:29:24 -0400 Subject: [PATCH 2/3] Polish --- ...reamingResponseBodyReturnValueHandler.java | 1 - ...ngResponseBodyReturnValueHandlerTests.java | 41 +++++++------------ 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBodyReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBodyReturnValueHandler.java index fd48719af1..a3b646125c 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBodyReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBodyReturnValueHandler.java @@ -74,7 +74,6 @@ public class StreamingResponseBodyReturnValueHandler implements HandlerMethodRet returnValue = responseEntity.getBody(); if (returnValue == null) { mavContainer.setRequestHandled(true); - // Ensure headers are flushed outputMessage.flush(); return; } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBodyReturnValueHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBodyReturnValueHandlerTests.java index 7d5ce1f1ff..30cf99957a 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBodyReturnValueHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBodyReturnValueHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -15,12 +15,9 @@ */ package org.springframework.web.servlet.mvc.method.annotation; -import static org.junit.Assert.*; - -import java.io.IOException; -import java.io.OutputStream; import java.lang.reflect.Method; import java.nio.charset.Charset; +import java.util.Collections; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -29,7 +26,6 @@ import org.junit.Before; import org.junit.Test; import org.springframework.core.MethodParameter; -import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockHttpServletResponse; @@ -40,6 +36,10 @@ import org.springframework.web.context.request.async.StandardServletAsyncWebRequ import org.springframework.web.context.request.async.WebAsyncUtils; import org.springframework.web.method.support.ModelAndViewContainer; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + /** * Unit tests for @@ -59,8 +59,6 @@ public class StreamingResponseBodyReturnValueHandlerTests { private MockHttpServletResponse response; - private HttpHeaders headers = new HttpHeaders(); - @Before public void setUp() throws Exception { @@ -72,13 +70,12 @@ public class StreamingResponseBodyReturnValueHandlerTests { this.response = new MockHttpServletResponse(); this.webRequest = new ServletWebRequest(this.request, this.response); - this.headers.add("foo", "bar"); - AsyncWebRequest asyncWebRequest = new StandardServletAsyncWebRequest(this.request, this.response); WebAsyncUtils.getAsyncManager(this.webRequest).setAsyncWebRequest(asyncWebRequest); this.request.setAsyncSupported(true); } + @Test public void supportsReturnType() throws Exception { assertTrue(this.handler.supportsReturnType(returnType(TestController.class, "handle"))); @@ -93,13 +90,9 @@ public class StreamingResponseBodyReturnValueHandlerTests { CountDownLatch latch = new CountDownLatch(1); MethodParameter returnType = returnType(TestController.class, "handle"); - StreamingResponseBody streamingBody = new StreamingResponseBody() { - - @Override - public void writeTo(OutputStream outputStream) throws IOException { - outputStream.write("foo".getBytes(Charset.forName("UTF-8"))); - latch.countDown(); - } + StreamingResponseBody streamingBody = outputStream -> { + outputStream.write("foo".getBytes(Charset.forName("UTF-8"))); + latch.countDown(); }; this.handler.handleReturnValue(streamingBody, returnType, this.mavContainer, this.webRequest); @@ -116,13 +109,9 @@ public class StreamingResponseBodyReturnValueHandlerTests { MethodParameter returnType = returnType(TestController.class, "handleResponseEntity"); ResponseEntity emitter = ResponseEntity.ok().header("foo", "bar") - .body(new StreamingResponseBody() { - - @Override - public void writeTo(OutputStream outputStream) throws IOException { - outputStream.write("foo".getBytes(Charset.forName("UTF-8"))); - latch.countDown(); - } + .body(outputStream -> { + outputStream.write("foo".getBytes(Charset.forName("UTF-8"))); + latch.countDown(); }); this.handler.handleReturnValue(emitter, returnType, this.mavContainer, this.webRequest); @@ -147,11 +136,11 @@ public class StreamingResponseBodyReturnValueHandlerTests { @Test public void responseEntityWithHeadersAndNoContent() throws Exception { + ResponseEntity emitter = ResponseEntity.noContent().header("foo", "bar").build(); MethodParameter returnType = returnType(TestController.class, "handleResponseEntity"); - ResponseEntity emitter = ResponseEntity.noContent().headers(headers).build(); this.handler.handleReturnValue(emitter, returnType, this.mavContainer, this.webRequest); - assertEquals(this.response.getHeaders("foo"), this.headers.get("foo")); + assertEquals(Collections.singletonList("bar"), this.response.getHeaders("foo")); } private MethodParameter returnType(Class clazz, String methodName) throws NoSuchMethodException { From f20f6c952aa9385d622fb1cd9ec6a4cc9023364c Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 31 May 2016 09:33:54 -0400 Subject: [PATCH 3/3] Flush headers after null ResponseBodyEmitter Issue: SPR-14315 --- .../annotation/ResponseBodyEmitterReturnValueHandler.java | 1 + .../ResponseBodyEmitterReturnValueHandlerTests.java | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java index 7930819c8a..264ca73ce8 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java @@ -137,6 +137,7 @@ public class ResponseBodyEmitterReturnValueHandler implements AsyncHandlerMethod returnValue = responseEntity.getBody(); if (returnValue == null) { mavContainer.setRequestHandled(true); + outputMessage.flush(); return; } } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandlerTests.java index 0ac243a5a4..bec3bfb1ba 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandlerTests.java @@ -21,6 +21,7 @@ import static org.springframework.web.servlet.mvc.method.annotation.SseEmitter.* import java.lang.reflect.Method; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicReference; @@ -184,11 +185,12 @@ public class ResponseBodyEmitterReturnValueHandlerTests { @Test public void responseEntitySseNoContent() throws Exception { MethodParameter returnType = returnType("handleResponseEntitySse"); - ResponseEntity entity = ResponseEntity.noContent().build(); + ResponseEntity entity = ResponseEntity.noContent().header("foo", "bar").build(); handleReturnValue(entity, returnType); assertFalse(this.request.isAsyncStarted()); assertEquals(204, this.response.getStatus()); + assertEquals(Collections.singletonList("bar"), this.response.getHeaders("foo")); } private void handleReturnValue(Object returnValue, MethodParameter returnType) throws Exception {