Skip to content

Commit 9247cd9

Browse files
committed
Don't let standalone Tomcat render its error page after redirect
Previously, if the configured error controller responded with a redirect to an error caused by an exception, standalone Tomcat would render its default error page for the original exception. This occurred because ErrorPageFilter sets the javax.servlet.error.exception request attribute prior to dispatching to the error controller and then does not clear it. As the request unwinds, Tomcat's ErrorReportValve notices that the attribute is set and renders an error page for the exception that is the attribute's value. This commit updates ErrorPageFilter to remove the javax.servlet.error.exception and javax.servlet.error.exception_type attributes upon successful completion of a forward to the error controller. This prevents Tomcat from rendering an error page for an exception that has already been handled by the error controller. Closes gh-7920
1 parent 3007443 commit 9247cd9

File tree

2 files changed

+86
-7
lines changed

2 files changed

+86
-7
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2016 the original author or authors.
2+
* Copyright 2012-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -183,6 +183,8 @@ private void forwardToErrorPage(String path, HttpServletRequest request,
183183
response.reset();
184184
response.sendError(500, ex.getMessage());
185185
request.getRequestDispatcher(path).forward(request, response);
186+
request.removeAttribute(ERROR_EXCEPTION);
187+
request.removeAttribute(ERROR_EXCEPTION_TYPE);
186188
}
187189

188190
private String getDescription(HttpServletRequest request) {

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

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2016 the original author or authors.
2+
* Copyright 2012-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,6 +17,9 @@
1717
package org.springframework.boot.web.support;
1818

1919
import java.io.IOException;
20+
import java.util.Enumeration;
21+
import java.util.HashMap;
22+
import java.util.Map;
2023

2124
import javax.servlet.RequestDispatcher;
2225
import javax.servlet.ServletException;
@@ -35,6 +38,7 @@
3538
import org.springframework.mock.web.MockFilterConfig;
3639
import org.springframework.mock.web.MockHttpServletRequest;
3740
import org.springframework.mock.web.MockHttpServletResponse;
41+
import org.springframework.mock.web.MockRequestDispatcher;
3842
import org.springframework.web.context.request.async.DeferredResult;
3943
import org.springframework.web.context.request.async.StandardServletAsyncWebRequest;
4044
import org.springframework.web.context.request.async.WebAsyncManager;
@@ -57,8 +61,7 @@ public class ErrorPageFilterTests {
5761

5862
private ErrorPageFilter filter = new ErrorPageFilter();
5963

60-
private MockHttpServletRequest request = new MockHttpServletRequest("GET",
61-
"/test/path");
64+
private DispatchRecordingMockHttpServletRequest request = new DispatchRecordingMockHttpServletRequest();
6265

6366
private MockHttpServletResponse response = new MockHttpServletResponse();
6467

@@ -261,8 +264,14 @@ public void doFilter(ServletRequest request, ServletResponse response)
261264
.isEqualTo(500);
262265
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_MESSAGE))
263266
.isEqualTo("BAD");
264-
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE))
267+
Map<String, Object> requestAttributes = getAttributesForDispatch("/500");
268+
assertThat(requestAttributes.get(RequestDispatcher.ERROR_EXCEPTION_TYPE))
265269
.isEqualTo(RuntimeException.class);
270+
assertThat(requestAttributes.get(RequestDispatcher.ERROR_EXCEPTION))
271+
.isInstanceOf(RuntimeException.class);
272+
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE))
273+
.isNull();
274+
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION)).isNull();
266275
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI))
267276
.isEqualTo("/test/path");
268277
assertThat(this.response.isCommitted()).isTrue();
@@ -318,8 +327,14 @@ public void doFilter(ServletRequest request, ServletResponse response)
318327
.isEqualTo(500);
319328
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_MESSAGE))
320329
.isEqualTo("BAD");
321-
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE))
330+
Map<String, Object> requestAttributes = getAttributesForDispatch("/500");
331+
assertThat(requestAttributes.get(RequestDispatcher.ERROR_EXCEPTION_TYPE))
322332
.isEqualTo(IllegalStateException.class);
333+
assertThat(requestAttributes.get(RequestDispatcher.ERROR_EXCEPTION))
334+
.isInstanceOf(IllegalStateException.class);
335+
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE))
336+
.isNull();
337+
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION)).isNull();
323338
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI))
324339
.isEqualTo("/test/path");
325340
assertThat(this.response.isCommitted()).isTrue();
@@ -492,8 +507,14 @@ public void doFilter(ServletRequest request, ServletResponse response)
492507
.isEqualTo(500);
493508
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_MESSAGE))
494509
.isEqualTo("BAD");
495-
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE))
510+
Map<String, Object> requestAttributes = getAttributesForDispatch("/500");
511+
assertThat(requestAttributes.get(RequestDispatcher.ERROR_EXCEPTION_TYPE))
496512
.isEqualTo(RuntimeException.class);
513+
assertThat(requestAttributes.get(RequestDispatcher.ERROR_EXCEPTION))
514+
.isInstanceOf(RuntimeException.class);
515+
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE))
516+
.isNull();
517+
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION)).isNull();
497518
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI))
498519
.isEqualTo("/test/path");
499520
assertThat(this.response.isCommitted()).isTrue();
@@ -510,4 +531,60 @@ private void setUpAsyncDispatch() throws Exception {
510531
asyncManager.startDeferredResultProcessing(result);
511532
}
512533

534+
private Map<String, Object> getAttributesForDispatch(String path) {
535+
return this.request.getDispatcher(path).getRequestAttributes();
536+
}
537+
538+
private static final class DispatchRecordingMockHttpServletRequest
539+
extends MockHttpServletRequest {
540+
541+
private final Map<String, AttributeCapturingRequestDispatcher> dispatchers = new HashMap<String, AttributeCapturingRequestDispatcher>();
542+
543+
private DispatchRecordingMockHttpServletRequest() {
544+
super("GET", "/test/path");
545+
}
546+
547+
@Override
548+
public RequestDispatcher getRequestDispatcher(String path) {
549+
AttributeCapturingRequestDispatcher dispatcher = new AttributeCapturingRequestDispatcher(
550+
path);
551+
this.dispatchers.put(path, dispatcher);
552+
return dispatcher;
553+
}
554+
555+
private AttributeCapturingRequestDispatcher getDispatcher(String path) {
556+
return this.dispatchers.get(path);
557+
}
558+
559+
private static final class AttributeCapturingRequestDispatcher
560+
extends MockRequestDispatcher {
561+
562+
private final Map<String, Object> requestAttributes = new HashMap<String, Object>();
563+
564+
private AttributeCapturingRequestDispatcher(String resource) {
565+
super(resource);
566+
}
567+
568+
@Override
569+
public void forward(ServletRequest request, ServletResponse response) {
570+
captureAttributes(request);
571+
super.forward(request, response);
572+
}
573+
574+
private void captureAttributes(ServletRequest request) {
575+
Enumeration<String> names = request.getAttributeNames();
576+
while (names.hasMoreElements()) {
577+
String name = names.nextElement();
578+
this.requestAttributes.put(name, request.getAttribute(name));
579+
}
580+
}
581+
582+
private Map<String, Object> getRequestAttributes() {
583+
return this.requestAttributes;
584+
}
585+
586+
}
587+
588+
}
589+
513590
}

0 commit comments

Comments
 (0)