18
18
19
19
import java .io .IOException ;
20
20
import java .io .PrintWriter ;
21
+ import java .util .Collection ;
22
+ import java .util .Collections ;
21
23
import java .util .HashMap ;
24
+ import java .util .HashSet ;
22
25
import java .util .Map ;
26
+ import java .util .Set ;
23
27
24
28
import javax .servlet .Filter ;
25
29
import javax .servlet .FilterChain ;
40
44
import org .springframework .boot .web .servlet .ErrorPageRegistry ;
41
45
import org .springframework .core .Ordered ;
42
46
import org .springframework .core .annotation .Order ;
47
+ import org .springframework .util .ClassUtils ;
43
48
import org .springframework .web .filter .OncePerRequestFilter ;
44
49
import org .springframework .web .util .NestedServletException ;
45
50
@@ -77,6 +82,14 @@ public class ErrorPageFilter implements Filter, ErrorPageRegistry {
77
82
78
83
private static final String ERROR_STATUS_CODE = "javax.servlet.error.status_code" ;
79
84
85
+ private static final Set <Class <?>> CLIENT_ABORT_EXCEPTIONS ;
86
+ static {
87
+ Set <Class <?>> clientAbortExceptions = new HashSet <>();
88
+ addClassIfPresent (clientAbortExceptions ,
89
+ "org.apache.catalina.connector.ClientAbortException" );
90
+ CLIENT_ABORT_EXCEPTIONS = Collections .unmodifiableSet (clientAbortExceptions );
91
+ }
92
+
80
93
private String global ;
81
94
82
95
private final Map <Integer , String > statuses = new HashMap <Integer , String >();
@@ -164,7 +177,6 @@ private void handleException(HttpServletRequest request, HttpServletResponse res
164
177
handleCommittedResponse (request , ex );
165
178
return ;
166
179
}
167
-
168
180
forwardToErrorPage (errorPath , request , wrapped , ex );
169
181
}
170
182
@@ -200,6 +212,9 @@ protected String getDescription(HttpServletRequest request) {
200
212
}
201
213
202
214
private void handleCommittedResponse (HttpServletRequest request , Throwable ex ) {
215
+ if (isClientAbortException (ex )) {
216
+ return ;
217
+ }
203
218
String message = "Cannot forward to error page for request "
204
219
+ getDescription (request ) + " as the response has already been"
205
220
+ " committed. As a result, the response may have the wrong status"
@@ -216,6 +231,18 @@ private void handleCommittedResponse(HttpServletRequest request, Throwable ex) {
216
231
}
217
232
}
218
233
234
+ private boolean isClientAbortException (Throwable ex ) {
235
+ if (ex == null ) {
236
+ return false ;
237
+ }
238
+ for (Class <?> candidate : CLIENT_ABORT_EXCEPTIONS ) {
239
+ if (candidate .isInstance (ex )) {
240
+ return true ;
241
+ }
242
+ }
243
+ return isClientAbortException (ex .getCause ());
244
+ }
245
+
219
246
private String getErrorPath (Map <Integer , String > map , Integer status ) {
220
247
if (map .containsKey (status )) {
221
248
return map .get (status );
@@ -276,6 +303,15 @@ else if (errorPage.getStatus() != null) {
276
303
public void destroy () {
277
304
}
278
305
306
+ private static void addClassIfPresent (Collection <Class <?>> collection ,
307
+ String className ) {
308
+ try {
309
+ collection .add (ClassUtils .forName (className , null ));
310
+ }
311
+ catch (Throwable ex ) {
312
+ }
313
+ }
314
+
279
315
private static class ErrorWrapperResponse extends HttpServletResponseWrapper {
280
316
281
317
private int status ;
0 commit comments