Skip to content

Commit 9252e74

Browse files
committed
Default webmvc handling of disconnected client errors
Closes gh-33753
1 parent 5271f5b commit 9252e74

File tree

3 files changed

+34
-2
lines changed

3 files changed

+34
-2
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerExceptionResolver.java

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
3131
import org.springframework.util.StringUtils;
3232
import org.springframework.web.servlet.HandlerExceptionResolver;
3333
import org.springframework.web.servlet.ModelAndView;
34+
import org.springframework.web.util.DisconnectedClientHelper;
3435

3536
/**
3637
* Abstract base class for {@link HandlerExceptionResolver} implementations.
@@ -48,6 +49,12 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti
4849

4950
private static final String HEADER_CACHE_CONTROL = "Cache-Control";
5051

52+
private static final String DISCONNECTED_CLIENT_LOG_CATEGORY =
53+
"org.springframework.web.servlet.handler.DisconnectedClient";
54+
55+
private static final DisconnectedClientHelper disconnectedClientHelper =
56+
new DisconnectedClientHelper(DISCONNECTED_CLIENT_LOG_CATEGORY);
57+
5158

5259
/** Logger available to subclasses. */
5360
protected final Log logger = LogFactory.getLog(getClass());
@@ -173,7 +180,7 @@ public ModelAndView resolveException(
173180
if (shouldApplyTo(request, handler)) {
174181
prepareResponse(ex, response);
175182
ModelAndView result = doResolveException(request, response, handler, ex);
176-
if (result != null) {
183+
if (result != null && !disconnectedClientHelper.checkAndLogClientDisconnectedException(ex)) {
177184
// Print debug message when warn logger is not enabled.
178185
if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
179186
logger.debug(buildLogMessage(ex, request) + (result.isEmpty() ? "" : " to " + result));

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java

+24
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.springframework.web.servlet.NoHandlerFoundException;
5252
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
5353
import org.springframework.web.servlet.resource.NoResourceFoundException;
54+
import org.springframework.web.util.DisconnectedClientHelper;
5455
import org.springframework.web.util.WebUtils;
5556

5657
/**
@@ -246,6 +247,9 @@ else if (ex instanceof AsyncRequestNotUsableException) {
246247
return handleAsyncRequestNotUsableException(
247248
(AsyncRequestNotUsableException) ex, request, response, handler);
248249
}
250+
else if (DisconnectedClientHelper.isClientDisconnectedException(ex)) {
251+
return handleDisconnectedClientException(ex, request, response, handler);
252+
}
249253
}
250254
catch (Exception handlerEx) {
251255
if (logger.isWarnEnabled()) {
@@ -514,6 +518,26 @@ protected ModelAndView handleAsyncRequestNotUsableException(AsyncRequestNotUsabl
514518
return new ModelAndView();
515519
}
516520

521+
/**
522+
* Handle an Exception that indicates the client has gone away. This is
523+
* typically an {@link IOException} of a specific subtype or with a message
524+
* specific to the underlying Servlet container. Those are detected through
525+
* {@link DisconnectedClientHelper#isClientDisconnectedException(Throwable)}
526+
* <p>By default, do nothing since the response is not usable.
527+
* @param ex the {@code Exception} to be handled
528+
* @param request current HTTP request
529+
* @param response current HTTP response
530+
* @param handler the executed handler, or {@code null} if none chosen
531+
* at the time of the exception (for example, if multipart resolution failed)
532+
* @return an empty ModelAndView indicating the exception was handled
533+
* @since 6.2
534+
*/
535+
protected ModelAndView handleDisconnectedClientException(
536+
Exception ex, HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) {
537+
538+
return new ModelAndView();
539+
}
540+
517541
/**
518542
* Handle an {@link ErrorResponse} exception.
519543
* <p>The default implementation sets status and the headers of the response

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java

+1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ void supportsAllDefaultHandlerExceptionResolverExceptionTypes() throws Exception
107107
Arrays.stream(DefaultHandlerExceptionResolver.class.getDeclaredMethods())
108108
.filter(method -> method.getName().startsWith("handle") && (method.getParameterCount() == 4))
109109
.filter(method -> !method.getName().equals("handleErrorResponse"))
110+
.filter(method -> !method.getName().equals("handleDisconnectedClientException"))
110111
.map(method -> method.getParameterTypes()[0])
111112
.forEach(exceptionType -> assertThat(annotation.value())
112113
.as("@ExceptionHandler is missing declaration for " + exceptionType.getName())

0 commit comments

Comments
 (0)