Last active
April 1, 2016 11:20
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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