Skip to content

Commit bacbff1

Browse files
committed
Do not flush response buffer in ErrorPageFilter if request is async
Previously, the ErrorPageFilter would always flush the response buffer, irrespective of the request being asynchronous. This could lead to a response being committed prematurely, preventing, for example, headers being set by subsequent processing. This commit updates ErrorPageFilter so that in the success case (status < 400) the response buffer is only flushed if the request is not async (determined by calling request.isAsyncStarted()). If an exception's been thrown or the status is >= 400 the response buffer is always flushed. Fixes #1316
1 parent 6f8d477 commit bacbff1

File tree

2 files changed

+68
-5
lines changed

2 files changed

+68
-5
lines changed

spring-boot/src/main/java/org/springframework/boot/context/web/ErrorPageFilter.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
@Component
5757
@Order(Ordered.HIGHEST_PRECEDENCE)
5858
class ErrorPageFilter extends AbstractConfigurableEmbeddedServletContainer implements
59-
Filter, NonEmbeddedServletContainerFactory {
59+
Filter, NonEmbeddedServletContainerFactory {
6060

6161
private static Log logger = LogFactory.getLog(ErrorPageFilter.class);
6262

@@ -109,18 +109,21 @@ private void doFilter(HttpServletRequest request, HttpServletResponse response,
109109
int status = wrapped.getStatus();
110110
if (status >= 400) {
111111
handleErrorStatus(request, response, status, wrapped.getMessage());
112+
response.flushBuffer();
113+
}
114+
else if (!request.isAsyncStarted()) {
115+
response.flushBuffer();
112116
}
113117
}
114118
catch (Throwable ex) {
115119
handleException(request, response, wrapped, ex);
120+
response.flushBuffer();
116121
}
117-
response.flushBuffer();
118-
119122
}
120123

121124
private void handleErrorStatus(HttpServletRequest request,
122125
HttpServletResponse response, int status, String message)
123-
throws ServletException, IOException {
126+
throws ServletException, IOException {
124127
String errorPath = getErrorPath(this.statuses, status);
125128
if (errorPath == null) {
126129
response.sendError(status, message);
@@ -132,7 +135,7 @@ private void handleErrorStatus(HttpServletRequest request,
132135

133136
private void handleException(HttpServletRequest request,
134137
HttpServletResponse response, ErrorWrapperResponse wrapped, Throwable ex)
135-
throws IOException, ServletException {
138+
throws IOException, ServletException {
136139
Class<?> type = ex.getClass();
137140
String errorPath = getErrorPath(type);
138141
if (errorPath == null) {

spring-boot/src/test/java/org/springframework/boot/context/web/ErrorPageFilterTests.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.springframework.mock.web.MockHttpServletResponse;
3535

3636
import static org.hamcrest.Matchers.equalTo;
37+
import static org.junit.Assert.assertFalse;
3738
import static org.junit.Assert.assertNotNull;
3839
import static org.junit.Assert.assertThat;
3940
import static org.junit.Assert.assertTrue;
@@ -59,6 +60,7 @@ public void notAnError() throws Exception {
5960
assertThat(this.chain.getRequest(), equalTo((ServletRequest) this.request));
6061
assertThat(((HttpServletResponseWrapper) this.chain.getResponse()).getResponse(),
6162
equalTo((ServletResponse) this.response));
63+
assertTrue(this.response.isCommitted());
6264
}
6365

6466
@Test
@@ -79,6 +81,7 @@ public void doFilter(ServletRequest request, ServletResponse response)
7981
equalTo((ServletResponse) this.response));
8082
assertThat(((HttpServletResponseWrapper) this.chain.getResponse()).getStatus(),
8183
equalTo(400));
84+
assertTrue(this.response.isCommitted());
8285
}
8386

8487
@Test
@@ -97,6 +100,7 @@ public void doFilter(ServletRequest request, ServletResponse response)
97100
equalTo((ServletResponse) this.response));
98101
assertThat(((HttpServletResponseWrapper) this.chain.getResponse()).getStatus(),
99102
equalTo(400));
103+
assertTrue(this.response.isCommitted());
100104
}
101105

102106
@Test
@@ -199,6 +203,62 @@ public void doFilter(ServletRequest request, ServletResponse response)
199203
equalTo((Object) "BAD"));
200204
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE),
201205
equalTo((Object) IllegalStateException.class.getName()));
206+
assertTrue(this.response.isCommitted());
207+
}
208+
209+
@Test
210+
public void responseIsNotCommitedWhenRequestIsAsync() throws Exception {
211+
this.request.setAsyncStarted(true);
212+
213+
this.filter.doFilter(this.request, this.response, this.chain);
214+
215+
assertThat(this.chain.getRequest(), equalTo((ServletRequest) this.request));
216+
assertThat(((HttpServletResponseWrapper) this.chain.getResponse()).getResponse(),
217+
equalTo((ServletResponse) this.response));
218+
assertFalse(this.response.isCommitted());
219+
}
220+
221+
@Test
222+
public void responseIsCommitedWhenRequestIsAsyncAndExceptionIsThrown()
223+
throws Exception {
224+
this.filter.addErrorPages(new ErrorPage("/error"));
225+
this.request.setAsyncStarted(true);
226+
this.chain = new MockFilterChain() {
227+
@Override
228+
public void doFilter(ServletRequest request, ServletResponse response)
229+
throws IOException, ServletException {
230+
super.doFilter(request, response);
231+
throw new RuntimeException("BAD");
232+
}
233+
};
234+
235+
this.filter.doFilter(this.request, this.response, this.chain);
236+
237+
assertThat(this.chain.getRequest(), equalTo((ServletRequest) this.request));
238+
assertThat(((HttpServletResponseWrapper) this.chain.getResponse()).getResponse(),
239+
equalTo((ServletResponse) this.response));
240+
assertTrue(this.response.isCommitted());
241+
}
242+
243+
@Test
244+
public void responseIsCommitedWhenRequestIsAsyncAndStatusIs400Plus() throws Exception {
245+
this.filter.addErrorPages(new ErrorPage("/error"));
246+
this.request.setAsyncStarted(true);
247+
this.chain = new MockFilterChain() {
248+
@Override
249+
public void doFilter(ServletRequest request, ServletResponse response)
250+
throws IOException, ServletException {
251+
super.doFilter(request, response);
252+
((HttpServletResponse) response).sendError(400, "BAD");
253+
}
254+
};
255+
256+
this.filter.doFilter(this.request, this.response, this.chain);
257+
258+
assertThat(this.chain.getRequest(), equalTo((ServletRequest) this.request));
259+
assertThat(((HttpServletResponseWrapper) this.chain.getResponse()).getResponse(),
260+
equalTo((ServletResponse) this.response));
261+
assertTrue(this.response.isCommitted());
202262
}
203263

204264
}

0 commit comments

Comments
 (0)