Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Suppress calls to sendError to prevent servlet containers from sending error pages to the client
package servletutils;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.filter.OncePerRequestFilter;
import com.google.common.base.Preconditions;
/**
* 1) Catches and logs unhandled exceptions
* 2) flushes the response buffer to prevent any Servlet3-spec error-page redirects by the servlet container
*/
public class UnhandledExceptionHandlerFilter extends OncePerRequestFilter {
public interface ExceptionHandler {
void handle(HttpServletRequest request, HttpServletResponse response, Integer sc, Throwable throwable);
}
private static class Slf4jLoggingExceptionHandler implements ExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(UnhandledExceptionHandlerFilter.class);
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, Integer sc, Throwable throwable) {
if (throwable != null) {
logger.error(String.format("Sending error response status %s for %s because of", sc, request.getRequestURI()),
throwable);
} else {
logger.warn(String.format("Sending error response status %s for %s", sc, request.getRequestURI()), new RuntimeException("stacktrace"));
}
}
}
private final ExceptionHandler exceptionHandler;
public UnhandledExceptionHandlerFilter() {
this(new Slf4jLoggingExceptionHandler());
}
public UnhandledExceptionHandlerFilter(ExceptionHandler exceptionHandler) {
Preconditions.checkNotNull(exceptionHandler);
this.exceptionHandler = exceptionHandler;
}
// suppress calls to sendError() and just setStatus() instead
// do NOT use sendError() otherwise per servlet spec the container will send an html error page
@SuppressWarnings({ "unused", "unchecked", "deprecation" })
private class StatusCodeCaptureWrapper extends HttpServletResponseWrapper {
private Integer statusCode;
private HttpServletRequest request;
private HttpServletResponse response;
public StatusCodeCaptureWrapper(HttpServletRequest request, HttpServletResponse response) {
super(response);
this.request = request;
this.response = response;
}
public Integer getStatusCode() {
return statusCode;
}
@Override
public void sendError(int sc) throws IOException {
// do NOT use sendError() otherwise per servlet spec the container will send an html error page
this.setStatus(sc);
exceptionHandler.handle(request, response, sc, (Throwable) request.getAttribute("javax.servlet.error.exception"));
}
@Override
public void sendError(int sc, String msg) throws IOException {
// do NOT use sendError() otherwise per servlet spec the container will send an html error page
this.setStatus(sc, msg);
exceptionHandler.handle(request, response, sc, (Throwable) request.getAttribute("javax.servlet.error.exception"));
}
@Override
public void setStatus(int sc) {
this.statusCode = sc;
super.setStatus(sc);
}
@Override
public void setStatus(int sc, String sm) {
this.statusCode = sc;
super.setStatus(sc, sm);
}
}
@Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException,
ServletException {
StatusCodeCaptureWrapper responseWrapper = new StatusCodeCaptureWrapper(request, response);
Throwable exception = null;
try {
chain.doFilter(request, responseWrapper);
} catch (ServletException e) {
exception = e.getRootCause();
} catch (Throwable e) { // NOSONAR this is an UnhandledExceptionHandler - we need to catch this
exception = e;
}
if (exception != null && !"ClientAbortException".equals(exception.getClass().getSimpleName())) {
ensureErrorStatusCodeSet(responseWrapper);
response.setStatus(responseWrapper.getStatusCode());
exceptionHandler.handle(request, response, responseWrapper.getStatusCode(), exception);
}
// flush to prevent servlet container to add anymore headers or content
response.flushBuffer();
}
private void ensureErrorStatusCodeSet(StatusCodeCaptureWrapper responseWrapper) {
if (responseWrapper.getStatusCode() == null) {
responseWrapper.setStatus(500);
}
}
@Override
public void destroy() {
// noop
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment