Improve async request timeout handling
Rather than setting the status to 503 directly from the timeout interceptor which no longer seems to work reliably with Servlet containers like Jetty even performing an additional ERROR dispatch back to the original URL, we know rather set the DeferredResult to an AsyncTimeoutException, which results in a dispatch and standard handling within Spring MVC. This should be a more reliable way of dealing with timeouts. Issue: SPR-14669
This commit is contained in:
@@ -44,6 +44,7 @@ import org.springframework.web.bind.ServletRequestBindingException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
|
||||
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
@@ -112,7 +113,8 @@ public abstract class ResponseEntityExceptionHandler {
|
||||
MethodArgumentNotValidException.class,
|
||||
MissingServletRequestPartException.class,
|
||||
BindException.class,
|
||||
NoHandlerFoundException.class
|
||||
NoHandlerFoundException.class,
|
||||
AsyncRequestTimeoutException.class
|
||||
})
|
||||
public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
@@ -172,6 +174,11 @@ public abstract class ResponseEntityExceptionHandler {
|
||||
HttpStatus status = HttpStatus.NOT_FOUND;
|
||||
return handleNoHandlerFoundException((NoHandlerFoundException) ex, headers, status, request);
|
||||
}
|
||||
else if (ex instanceof AsyncRequestTimeoutException) {
|
||||
HttpStatus status = HttpStatus.SERVICE_UNAVAILABLE;
|
||||
return handleAsyncRequestTimeoutException(
|
||||
(AsyncRequestTimeoutException) ex, headers, status, request);
|
||||
}
|
||||
else {
|
||||
logger.warn("Unknown exception type: " + ex.getClass().getName());
|
||||
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
@@ -424,4 +431,20 @@ public abstract class ResponseEntityExceptionHandler {
|
||||
return handleExceptionInternal(ex, null, headers, status, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the response for NoHandlerFoundException.
|
||||
* <p>This method delegates to {@link #handleExceptionInternal}.
|
||||
* @param ex the exception
|
||||
* @param headers the headers to be written to the response
|
||||
* @param status the selected response status
|
||||
* @param request the current request
|
||||
* @return a {@code ResponseEntity} instance
|
||||
* @since 4.2.8
|
||||
*/
|
||||
protected ResponseEntity<Object> handleAsyncRequestTimeoutException(
|
||||
AsyncRequestTimeoutException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
|
||||
|
||||
return handleExceptionInternal(ex, null, headers, status, request);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.springframework.web.bind.ServletRequestBindingException;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
@@ -153,6 +154,10 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
|
||||
else if (ex instanceof NoHandlerFoundException) {
|
||||
return handleNoHandlerFoundException((NoHandlerFoundException) ex, request, response, handler);
|
||||
}
|
||||
else if (ex instanceof AsyncRequestTimeoutException) {
|
||||
return handleAsyncRequestTimeoutException(
|
||||
(AsyncRequestTimeoutException) ex, request, response, handler);
|
||||
}
|
||||
}
|
||||
catch (Exception handlerException) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
@@ -449,6 +454,25 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
|
||||
return new ModelAndView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case where an async request timed out.
|
||||
* <p>The default implementation sends an HTTP 503 error.
|
||||
* @param ex the {@link AsyncRequestTimeoutException }to be handled
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler the executed handler, or {@code null} if none chosen
|
||||
* at the time of the exception (for example, if multipart resolution failed)
|
||||
* @return an empty ModelAndView indicating the exception was handled
|
||||
* @throws IOException potentially thrown from response.sendError()
|
||||
* @since 4.2.8
|
||||
*/
|
||||
protected ModelAndView handleAsyncRequestTimeoutException(AsyncRequestTimeoutException ex,
|
||||
HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
|
||||
|
||||
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
|
||||
return new ModelAndView();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invoked to send a server error. Sets the status to 500 and also sets the
|
||||
|
||||
Reference in New Issue
Block a user