Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RestClientException should not be considered as client disconnected exception #34269

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -24,6 +24,7 @@

import org.springframework.core.NestedExceptionUtils;
import org.springframework.util.Assert;
import org.springframework.web.client.RestClientException;

/**
* Utility methods to assist with identifying and logging exceptions that indicate
Expand All @@ -32,6 +33,7 @@
* and a full stacktrace at TRACE level.
*
* @author Rossen Stoyanchev
* @author Yanming Zhou
* @since 6.1
*/
public class DisconnectedClientHelper {
Expand Down Expand Up @@ -83,6 +85,13 @@ else if (logger.isDebugEnabled()) {
* </ul>
*/
public static boolean isClientDisconnectedException(Throwable ex) {
Throwable cause = ex;
while (cause != null) {
if (cause instanceof RestClientException) {
return false;
}
cause = cause.getCause();
}
String message = NestedExceptionUtils.getMostSpecificCause(ex).getMessage();
if (message != null) {
String text = message.toLowerCase(Locale.ROOT);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -51,7 +51,6 @@
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
import org.springframework.web.servlet.resource.NoResourceFoundException;
import org.springframework.web.util.DisconnectedClientHelper;
import org.springframework.web.util.WebUtils;

/**
Expand Down Expand Up @@ -149,6 +148,7 @@
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @author Yanming Zhou
* @since 3.0
* @see org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
*/
Expand Down Expand Up @@ -246,9 +246,6 @@ else if (ex instanceof AsyncRequestNotUsableException) {
return handleAsyncRequestNotUsableException(
(AsyncRequestNotUsableException) ex, request, response, handler);
}
else if (DisconnectedClientHelper.isClientDisconnectedException(ex)) {
return handleDisconnectedClientException(ex, request, response, handler);
}
}
catch (Exception handlerEx) {
if (logger.isWarnEnabled()) {
Expand Down Expand Up @@ -505,26 +502,6 @@ protected ModelAndView handleAsyncRequestNotUsableException(AsyncRequestNotUsabl
return new ModelAndView();
}

/**
* Handle an Exception that indicates the client has gone away. This is
* typically an {@link IOException} of a specific subtype or with a message
* specific to the underlying Servlet container. Those are detected through
* {@link DisconnectedClientHelper#isClientDisconnectedException(Throwable)}
* <p>By default, do nothing since the response is not usable.
* @param ex the {@code Exception} 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
* @since 6.2
*/
protected ModelAndView handleDisconnectedClientException(
Exception ex, HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) {

return new ModelAndView();
}

/**
* Handle an {@link ErrorResponse} exception.
* <p>The default implementation sets status and the headers of the response
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,6 +17,7 @@
package org.springframework.web.servlet.mvc.support;

import java.lang.reflect.Method;
import java.net.SocketException;
import java.util.Arrays;
import java.util.Collections;

Expand All @@ -42,6 +43,7 @@
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
Expand All @@ -60,6 +62,7 @@
*
* @author Arjen Poutsma
* @author Sebastien Deleuze
* @author Yanming Zhou
*/
class DefaultHandlerExceptionResolverTests {

Expand Down Expand Up @@ -248,6 +251,17 @@ void handleMaxUploadSizeExceededException() {
assertThat(response.getErrorMessage()).isEqualTo("Maximum upload size exceeded");
}

@Test
void handleRestClientExceptionHasConnectionResetMessage() {
RestClientException ex = new RestClientException("I/O error", new SocketException("Connection reset"));
ModelAndView mav = exceptionResolver.resolveException(request, response, null, ex);
assertThat(mav).as("ModelAndView is returned").isNull();

Exception exception = new Exception(ex.getMessage(), ex);
mav = exceptionResolver.resolveException(request, response, null, exception);
assertThat(mav).as("ModelAndView is returned").isNull();
}

@Test
void customModelAndView() {
ModelAndView expected = new ModelAndView();
Expand Down
Loading