1616
1717package  org .springframework .web .util ;
1818
19+ import  java .util .HashSet ;
1920import  java .util .Locale ;
2021import  java .util .Set ;
2122
2425
2526import  org .springframework .core .NestedExceptionUtils ;
2627import  org .springframework .util .Assert ;
28+ import  org .springframework .util .ClassUtils ;
2729
2830/** 
29-  * Utility methods to assist with identifying and logging exceptions that indicate 
30-  * the client has gone away. Such exceptions fill logs with unnecessary stack 
31-  * traces. The utility methods help to log a single line message at DEBUG level, 
32-  * and a full stacktrace at TRACE level. 
31+  * Utility methods to assist with identifying and logging exceptions that 
32+  * indicate the server response connection is lost, for example because the 
33+  * client has gone away. This class helps to identify such exceptions and 
34+  * minimize logging to a single line at DEBUG level, while making the full 
35+  * error stacktrace at TRACE level. 
3336 * 
3437 * @author Rossen Stoyanchev 
3538 * @since 6.1 
3639 */ 
3740public  class  DisconnectedClientHelper  {
3841
39- 	// Look for server response connection issues (aborted), not onward connections 
40- 	// to other servers (500 errors). 
41- 
4242	private  static  final  Set <String > EXCEPTION_PHRASES  =
4343			Set .of ("broken pipe" , "connection reset by peer" );
4444
4545	private  static  final  Set <String > EXCEPTION_TYPE_NAMES  =
4646			Set .of ("AbortedException" , "ClientAbortException" ,
4747					"EOFException" , "EofException" , "AsyncRequestNotUsableException" );
4848
49+ 	private  static  final  Set <Class <?>> CLIENT_EXCEPTION_TYPES  = new  HashSet <>(2 );
50+ 
51+ 	static  {
52+ 		try  {
53+ 			ClassLoader  classLoader  = DisconnectedClientHelper .class .getClassLoader ();
54+ 			CLIENT_EXCEPTION_TYPES .add (ClassUtils .forName (
55+ 					"org.springframework.web.client.RestClientException" , classLoader ));
56+ 			CLIENT_EXCEPTION_TYPES .add (ClassUtils .forName (
57+ 					"org.springframework.web.reactive.function.client.WebClientException" , classLoader ));
58+ 		}
59+ 		catch  (ClassNotFoundException  ex ) {
60+ 			// ignore 
61+ 		}
62+ 	}
63+ 
64+ 
4965	private  final  Log  logger ;
5066
5167
@@ -85,6 +101,22 @@ else if (logger.isDebugEnabled()) {
85101	 * </ul> 
86102	 */ 
87103	public  static  boolean  isClientDisconnectedException (Throwable  ex ) {
104+ 		Throwable  currentEx  = ex ;
105+ 		Throwable  lastEx  = null ;
106+ 		while  (currentEx  != null  && currentEx  != lastEx ) {
107+ 			// Ignore onward connection issues to other servers (500 error) 
108+ 			for  (Class <?> exceptionType  : CLIENT_EXCEPTION_TYPES ) {
109+ 				if  (exceptionType .isInstance (currentEx )) {
110+ 					return  false ;
111+ 				}
112+ 			}
113+ 			if  (EXCEPTION_TYPE_NAMES .contains (currentEx .getClass ().getSimpleName ())) {
114+ 				return  true ;
115+ 			}
116+ 			lastEx  = currentEx ;
117+ 			currentEx  = currentEx .getCause ();
118+ 		}
119+ 
88120		String  message  = NestedExceptionUtils .getMostSpecificCause (ex ).getMessage ();
89121		if  (message  != null ) {
90122			String  text  = message .toLowerCase (Locale .ROOT );
@@ -94,7 +126,8 @@ public static boolean isClientDisconnectedException(Throwable ex) {
94126				}
95127			}
96128		}
97- 		return  EXCEPTION_TYPE_NAMES .contains (ex .getClass ().getSimpleName ());
129+ 
130+ 		return  false ;
98131	}
99132
100133}
0 commit comments