Skip to content

Instantly share code, notes, and snippets.

@eeichinger
Last active April 1, 2016 11:20
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save eeichinger/4545911 to your computer and use it in GitHub Desktop.
Save eeichinger/4545911 to your computer and use it in GitHub Desktop.
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