MockMvc re-uses response instance on async dispatch
MockMvc now properly detects the presence of an AsyncContext and re-uses the response instance used to start it. This commit also includes a minor fix in ResponseBodyEmitterReturnValueHandler to ensure it does not disable ETag related content buffering for reactive return values that do not result in streaming (e.g. single value or collections). Issue: SPR-16067
This commit is contained in:
@@ -18,9 +18,13 @@ package org.springframework.test.web.servlet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
import org.springframework.beans.Mergeable;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -135,24 +139,35 @@ public final class MockMvc {
|
||||
}
|
||||
|
||||
MockHttpServletRequest request = requestBuilder.buildRequest(this.servletContext);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
AsyncContext asyncContext = request.getAsyncContext();
|
||||
MockHttpServletResponse mockResponse;
|
||||
HttpServletResponse servletResponse;
|
||||
if (asyncContext != null) {
|
||||
servletResponse = (HttpServletResponse) asyncContext.getResponse();
|
||||
mockResponse = unwrapResponseIfNecessary(servletResponse);
|
||||
}
|
||||
else {
|
||||
mockResponse = new MockHttpServletResponse();
|
||||
servletResponse = mockResponse;
|
||||
}
|
||||
|
||||
if (requestBuilder instanceof SmartRequestBuilder) {
|
||||
request = ((SmartRequestBuilder) requestBuilder).postProcessRequest(request);
|
||||
}
|
||||
|
||||
final MvcResult mvcResult = new DefaultMvcResult(request, response);
|
||||
final MvcResult mvcResult = new DefaultMvcResult(request, mockResponse);
|
||||
request.setAttribute(MVC_RESULT_ATTRIBUTE, mvcResult);
|
||||
|
||||
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
|
||||
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response));
|
||||
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, servletResponse));
|
||||
|
||||
MockFilterChain filterChain = new MockFilterChain(this.servlet, this.filters);
|
||||
filterChain.doFilter(request, response);
|
||||
filterChain.doFilter(request, servletResponse);
|
||||
|
||||
if (DispatcherType.ASYNC.equals(request.getDispatcherType()) &&
|
||||
request.getAsyncContext() != null & !request.isAsyncStarted()) {
|
||||
request.getAsyncContext().complete();
|
||||
asyncContext != null & !request.isAsyncStarted()) {
|
||||
asyncContext.complete();
|
||||
}
|
||||
|
||||
applyDefaultResultActions(mvcResult);
|
||||
@@ -176,6 +191,14 @@ public final class MockMvc {
|
||||
};
|
||||
}
|
||||
|
||||
private MockHttpServletResponse unwrapResponseIfNecessary(ServletResponse servletResponse) {
|
||||
while (servletResponse instanceof HttpServletResponseWrapper) {
|
||||
servletResponse = ((HttpServletResponseWrapper) servletResponse).getResponse();
|
||||
}
|
||||
Assert.isInstanceOf(MockHttpServletResponse.class, servletResponse);
|
||||
return (MockHttpServletResponse) servletResponse;
|
||||
}
|
||||
|
||||
|
||||
private void applyDefaultResultActions(MvcResult mvcResult) throws Exception {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user