ErrorPageFilterTests Class — spring-boot Architecture
Architecture documentation for the ErrorPageFilterTests class in ErrorPageFilterTests.java from the spring-boot codebase.
Entity Profile
Relationship Graph
Source Code
core/spring-boot/src/test/java/org/springframework/boot/web/servlet/support/ErrorPageFilterTests.java lines 63–529
@ExtendWith(OutputCaptureExtension.class)
class ErrorPageFilterTests {
private final ErrorPageFilter filter = new ErrorPageFilter();
private final DispatchRecordingMockHttpServletRequest request = new DispatchRecordingMockHttpServletRequest();
private final MockHttpServletResponse response = new MockHttpServletResponse();
private MockFilterChain chain = new TestFilterChain((request, response, chain) -> {
});
@Test
void notAnError() throws Exception {
this.filter.doFilter(this.request, this.response, this.chain);
assertThat(this.chain.getRequest()).isEqualTo(this.request);
assertThat(getChainResponse().getResponse()).isEqualTo(this.response);
assertThat(this.response.isCommitted()).isTrue();
assertThat(this.response.getForwardedUrl()).isNull();
}
@Test
void notAnErrorButNotOK() throws Exception {
this.chain = new TestFilterChain((request, response, chain) -> {
response.setStatus(201);
chain.call();
response.flushBuffer();
});
this.filter.doFilter(this.request, this.response, this.chain);
assertThat(getChainResponse().getStatus()).isEqualTo(201);
assertThat(((HttpServletResponse) getChainResponse().getResponse()).getStatus()).isEqualTo(201);
assertThat(this.response.isCommitted()).isTrue();
}
@Test
void unauthorizedWithErrorPath() throws Exception {
this.filter.addErrorPages(new ErrorPage("/error"));
this.chain = new TestFilterChain((request, response, chain) -> response.sendError(401, "UNAUTHORIZED"));
this.filter.doFilter(this.request, this.response, this.chain);
assertThat(this.chain.getRequest()).isEqualTo(this.request);
HttpServletResponseWrapper wrapper = getChainResponse();
assertThat(wrapper.getResponse()).isEqualTo(this.response);
assertThat(this.response.isCommitted()).isTrue();
assertThat(wrapper.getStatus()).isEqualTo(401);
// The real response has to be 401 as well...
assertThat(this.response.getStatus()).isEqualTo(401);
assertThat(this.response.getForwardedUrl()).isEqualTo("/error");
}
@Test
void responseCommitted() throws Exception {
this.filter.addErrorPages(new ErrorPage("/error"));
this.response.setCommitted(true);
this.chain = new TestFilterChain((request, response, chain) -> response.sendError(400, "BAD"));
this.filter.doFilter(this.request, this.response, this.chain);
assertThat(this.chain.getRequest()).isEqualTo(this.request);
assertThat((getChainResponse()).getResponse()).isEqualTo(this.response);
assertThat((getChainResponse()).getStatus()).isEqualTo(400);
assertThat(this.response.getForwardedUrl()).isNull();
assertThat(this.response.isCommitted()).isTrue();
}
@Test
void responseCommittedWhenFromClientAbortException(CapturedOutput output) throws Exception {
this.filter.addErrorPages(new ErrorPage("/error"));
this.response.setCommitted(true);
this.chain = new TestFilterChain((request, response, chain) -> {
chain.call();
throw new ClientAbortException();
});
this.filter.doFilter(this.request, this.response, this.chain);
assertThat(this.response.isCommitted()).isTrue();
assertThat(output).doesNotContain("Cannot forward");
}
@Test
void responseUncommittedWithoutErrorPage() throws Exception {
this.chain = new TestFilterChain((request, response, chain) -> response.sendError(400, "BAD"));
this.filter.doFilter(this.request, this.response, this.chain);
assertThat(this.chain.getRequest()).isEqualTo(this.request);
assertThat((getChainResponse()).getResponse()).isEqualTo(this.response);
assertThat((getChainResponse()).getStatus()).isEqualTo(400);
assertThat(this.response.getForwardedUrl()).isNull();
assertThat(this.response.isCommitted()).isTrue();
}
@Test
void oncePerRequest() throws Exception {
this.chain = new TestFilterChain((request, response, chain) -> {
response.sendError(400, "BAD");
assertThat(request.getAttribute("FILTER.FILTERED")).isNotNull();
});
this.filter.init(new MockFilterConfig("FILTER"));
this.filter.doFilter(this.request, this.response, this.chain);
}
@Test
void globalError() throws Exception {
this.filter.addErrorPages(new ErrorPage("/error"));
this.chain = new TestFilterChain((request, response, chain) -> response.sendError(400, "BAD"));
this.filter.doFilter(this.request, this.response, this.chain);
assertThat((getChainResponse()).getStatus()).isEqualTo(400);
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)).isEqualTo(400);
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_MESSAGE)).isEqualTo("BAD");
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI)).isEqualTo("/test/path");
assertThat(this.response.isCommitted()).isTrue();
assertThat(this.response.getForwardedUrl()).isEqualTo("/error");
}
@Test
void statusError() throws Exception {
this.filter.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
this.chain = new TestFilterChain((request, response, chain) -> response.sendError(400, "BAD"));
this.filter.doFilter(this.request, this.response, this.chain);
assertThat((getChainResponse()).getStatus()).isEqualTo(400);
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)).isEqualTo(400);
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_MESSAGE)).isEqualTo("BAD");
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI)).isEqualTo("/test/path");
assertThat(this.response.isCommitted()).isTrue();
assertThat(this.response.getForwardedUrl()).isEqualTo("/400");
}
@Test
void statusErrorWithCommittedResponse() throws Exception {
this.filter.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
this.chain = new TestFilterChain((request, response, chain) -> {
response.sendError(400, "BAD");
response.flushBuffer();
});
this.filter.doFilter(this.request, this.response, this.chain);
assertThat((getChainResponse()).getStatus()).isEqualTo(400);
assertThat(this.response.isCommitted()).isTrue();
assertThat(this.response.getForwardedUrl()).isNull();
}
@Test
void exceptionError() throws Exception {
this.filter.addErrorPages(new ErrorPage(RuntimeException.class, "/500"));
this.chain = new TestFilterChain((request, response, chain) -> {
chain.call();
throw new RuntimeException("BAD");
});
this.filter.doFilter(this.request, this.response, this.chain);
assertThat((getChainResponse()).getStatus()).isEqualTo(500);
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)).isEqualTo(500);
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_MESSAGE)).isEqualTo("BAD");
Map<String, Object> requestAttributes = getAttributesForDispatch("/500");
assertThat(requestAttributes).containsEntry(RequestDispatcher.ERROR_EXCEPTION_TYPE, RuntimeException.class);
assertThat(requestAttributes.get(RequestDispatcher.ERROR_EXCEPTION)).isInstanceOf(RuntimeException.class);
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE)).isNull();
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION)).isNull();
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI)).isEqualTo("/test/path");
assertThat(this.response.isCommitted()).isTrue();
assertThat(this.response.getForwardedUrl()).isEqualTo("/500");
}
@Test
void exceptionErrorWithCommittedResponse() throws Exception {
this.filter.addErrorPages(new ErrorPage(RuntimeException.class, "/500"));
this.chain = new TestFilterChain((request, response, chain) -> {
chain.call();
response.flushBuffer();
throw new RuntimeException("BAD");
});
this.filter.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getForwardedUrl()).isNull();
}
@Test
void statusCode() throws Exception {
this.chain = new TestFilterChain((request, response, chain) -> assertThat(response.getStatus()).isEqualTo(200));
this.filter.doFilter(this.request, this.response, this.chain);
assertThat((getChainResponse()).getStatus()).isEqualTo(200);
}
@Test
void subClassExceptionError() throws Exception {
this.filter.addErrorPages(new ErrorPage(RuntimeException.class, "/500"));
this.chain = new TestFilterChain((request, response, chain) -> {
chain.call();
throw new IllegalStateException("BAD");
});
this.filter.doFilter(this.request, this.response, this.chain);
assertThat((getChainResponse()).getStatus()).isEqualTo(500);
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)).isEqualTo(500);
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_MESSAGE)).isEqualTo("BAD");
Map<String, Object> requestAttributes = getAttributesForDispatch("/500");
assertThat(requestAttributes).containsEntry(RequestDispatcher.ERROR_EXCEPTION_TYPE,
IllegalStateException.class);
assertThat(requestAttributes.get(RequestDispatcher.ERROR_EXCEPTION)).isInstanceOf(IllegalStateException.class);
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE)).isNull();
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION)).isNull();
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI)).isEqualTo("/test/path");
assertThat(this.response.isCommitted()).isTrue();
}
@Test
void responseIsNotCommittedWhenRequestIsAsync() throws Exception {
this.request.setAsyncStarted(true);
this.filter.doFilter(this.request, this.response, this.chain);
assertThat(this.chain.getRequest()).isEqualTo(this.request);
assertThat((getChainResponse()).getResponse()).isEqualTo(this.response);
assertThat(this.response.isCommitted()).isFalse();
}
@Test
void responseIsCommittedWhenRequestIsAsyncAndExceptionIsThrown() throws Exception {
this.filter.addErrorPages(new ErrorPage("/error"));
this.request.setAsyncStarted(true);
this.chain = new TestFilterChain((request, response, chain) -> {
chain.call();
throw new RuntimeException("BAD");
});
this.filter.doFilter(this.request, this.response, this.chain);
assertThat(this.chain.getRequest()).isEqualTo(this.request);
assertThat((getChainResponse()).getResponse()).isEqualTo(this.response);
assertThat(this.response.isCommitted()).isTrue();
}
@Test
void responseIsCommittedWhenRequestIsAsyncAndStatusIs400Plus() throws Exception {
this.filter.addErrorPages(new ErrorPage("/error"));
this.request.setAsyncStarted(true);
this.chain = new TestFilterChain((request, response, chain) -> {
chain.call();
response.sendError(400, "BAD");
});
this.filter.doFilter(this.request, this.response, this.chain);
assertThat(this.chain.getRequest()).isEqualTo(this.request);
assertThat((getChainResponse()).getResponse()).isEqualTo(this.response);
assertThat(this.response.isCommitted()).isTrue();
}
@Test
void responseIsNotCommittedDuringAsyncDispatch() throws Exception {
setUpAsyncDispatch();
this.filter.doFilter(this.request, this.response, this.chain);
assertThat(this.chain.getRequest()).isEqualTo(this.request);
assertThat((getChainResponse()).getResponse()).isEqualTo(this.response);
assertThat(this.response.isCommitted()).isFalse();
}
@Test
void responseIsCommittedWhenExceptionIsThrownDuringAsyncDispatch() throws Exception {
this.filter.addErrorPages(new ErrorPage("/error"));
setUpAsyncDispatch();
this.chain = new TestFilterChain((request, response, chain) -> {
chain.call();
throw new RuntimeException("BAD");
});
this.filter.doFilter(this.request, this.response, this.chain);
assertThat(this.chain.getRequest()).isEqualTo(this.request);
assertThat((getChainResponse()).getResponse()).isEqualTo(this.response);
assertThat(this.response.isCommitted()).isTrue();
}
@Test
void responseIsCommittedWhenStatusIs400PlusDuringAsyncDispatch() throws Exception {
this.filter.addErrorPages(new ErrorPage("/error"));
setUpAsyncDispatch();
this.chain = new TestFilterChain((request, response, chain) -> {
chain.call();
response.sendError(400, "BAD");
});
this.filter.doFilter(this.request, this.response, this.chain);
assertThat(this.chain.getRequest()).isEqualTo(this.request);
assertThat((getChainResponse()).getResponse()).isEqualTo(this.response);
assertThat(this.response.isCommitted()).isTrue();
}
@Test
void responseIsNotFlushedIfStatusIsLessThan400AndItHasAlreadyBeenCommitted() throws Exception {
HttpServletResponse committedResponse = mock(HttpServletResponse.class);
given(committedResponse.isCommitted()).willReturn(true);
given(committedResponse.getStatus()).willReturn(200);
this.filter.doFilter(this.request, committedResponse, this.chain);
then(committedResponse).should(never()).flushBuffer();
}
@Test
void errorMessageForRequestWithoutPathInfo(CapturedOutput output) throws IOException, ServletException {
this.request.setServletPath("/test");
this.filter.addErrorPages(new ErrorPage("/error"));
this.chain = new TestFilterChain((request, response, chain) -> {
chain.call();
throw new RuntimeException();
});
this.filter.doFilter(this.request, this.response, this.chain);
assertThat(output).contains("request [/test]");
}
@Test
void errorMessageForRequestWithPathInfo(CapturedOutput output) throws IOException, ServletException {
this.request.setServletPath("/test");
this.request.setPathInfo("/alpha");
this.filter.addErrorPages(new ErrorPage("/error"));
this.chain = new TestFilterChain((request, response, chain) -> {
chain.call();
throw new RuntimeException();
});
this.filter.doFilter(this.request, this.response, this.chain);
assertThat(output).contains("request [/test/alpha]");
}
@Test
void servletExceptionIsUnwrapped() throws Exception {
this.filter.addErrorPages(new ErrorPage(RuntimeException.class, "/500"));
this.chain = new TestFilterChain((request, response, chain) -> {
chain.call();
throw new ServletException("Wrapper", new RuntimeException("BAD"));
});
this.filter.doFilter(this.request, this.response, this.chain);
assertThat((getChainResponse()).getStatus()).isEqualTo(500);
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)).isEqualTo(500);
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_MESSAGE)).isEqualTo("BAD");
Map<String, Object> requestAttributes = getAttributesForDispatch("/500");
assertThat(requestAttributes).containsEntry(RequestDispatcher.ERROR_EXCEPTION_TYPE, RuntimeException.class);
assertThat(requestAttributes.get(RequestDispatcher.ERROR_EXCEPTION)).isInstanceOf(RuntimeException.class);
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE)).isNull();
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION)).isNull();
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI)).isEqualTo("/test/path");
assertThat(this.response.isCommitted()).isTrue();
assertThat(this.response.getForwardedUrl()).isEqualTo("/500");
}
@Test
void servletExceptionWithNoCause() throws Exception {
this.filter.addErrorPages(new ErrorPage(MissingServletRequestParameterException.class, "/500"));
this.chain = new TestFilterChain((request, response, chain) -> {
chain.call();
throw new MissingServletRequestParameterException("test", "string");
});
this.filter.doFilter(this.request, this.response, this.chain);
assertThat((getChainResponse()).getStatus()).isEqualTo(500);
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)).isEqualTo(500);
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_MESSAGE))
.isEqualTo("Required request parameter 'test' for method parameter type string is not present");
Map<String, Object> requestAttributes = getAttributesForDispatch("/500");
assertThat(requestAttributes).containsEntry(RequestDispatcher.ERROR_EXCEPTION_TYPE,
MissingServletRequestParameterException.class);
assertThat(requestAttributes.get(RequestDispatcher.ERROR_EXCEPTION))
.isInstanceOf(MissingServletRequestParameterException.class);
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE)).isNull();
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_EXCEPTION)).isNull();
assertThat(this.request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI)).isEqualTo("/test/path");
assertThat(this.response.isCommitted()).isTrue();
assertThat(this.response.getForwardedUrl()).isEqualTo("/500");
}
@Test
void whenErrorIsSentAndWriterIsFlushedErrorIsSentToTheClient() throws Exception {
this.chain = new TestFilterChain((request, response, chain) -> {
response.sendError(400);
response.getWriter().flush();
});
this.filter.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(400);
}
private void setUpAsyncDispatch() throws Exception {
this.request.setAsyncSupported(true);
this.request.setAsyncStarted(true);
DeferredResult<String> result = new DeferredResult<>();
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(this.request);
asyncManager.setAsyncWebRequest(new StandardServletAsyncWebRequest(this.request, this.response));
asyncManager.startDeferredResultProcessing(result);
}
private Map<String, Object> getAttributesForDispatch(String path) {
return this.request.getDispatcher(path).getRequestAttributes();
}
private HttpServletResponseWrapper getChainResponse() {
HttpServletResponseWrapper response = (HttpServletResponseWrapper) this.chain.getResponse();
assertThat(response).isNotNull();
return response;
}
static class TestFilterChain extends MockFilterChain {
private final FilterHandler handler;
TestFilterChain(FilterHandler handler) {
this.handler = handler;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
AtomicBoolean called = new AtomicBoolean();
Chain chain = () -> {
if (called.compareAndSet(false, true)) {
super.doFilter(request, response);
}
};
this.handler.handle((HttpServletRequest) request, (HttpServletResponse) response, chain);
chain.call();
}
}
@FunctionalInterface
interface FilterHandler {
void handle(HttpServletRequest request, HttpServletResponse response, Chain chain)
throws IOException, ServletException;
}
@FunctionalInterface
interface Chain {
void call() throws IOException, ServletException;
}
private static final class DispatchRecordingMockHttpServletRequest extends MockHttpServletRequest {
private final Map<String, AttributeCapturingRequestDispatcher> dispatchers = new HashMap<>();
private DispatchRecordingMockHttpServletRequest() {
super("GET", "/test/path");
}
@Override
public RequestDispatcher getRequestDispatcher(String path) {
AttributeCapturingRequestDispatcher dispatcher = new AttributeCapturingRequestDispatcher(path);
this.dispatchers.put(path, dispatcher);
return dispatcher;
}
private AttributeCapturingRequestDispatcher getDispatcher(String path) {
AttributeCapturingRequestDispatcher dispatcher = this.dispatchers.get(path);
assertThat(dispatcher).isNotNull();
return dispatcher;
}
private static final class AttributeCapturingRequestDispatcher extends MockRequestDispatcher {
private final Map<String, Object> requestAttributes = new HashMap<>();
private AttributeCapturingRequestDispatcher(String resource) {
super(resource);
}
@Override
public void forward(ServletRequest request, ServletResponse response) {
captureAttributes(request);
super.forward(request, response);
}
private void captureAttributes(ServletRequest request) {
Enumeration<String> names = request.getAttributeNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
this.requestAttributes.put(name, request.getAttribute(name));
}
}
private Map<String, Object> getRequestAttributes() {
return this.requestAttributes;
}
}
}
}
Domain
Source
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free