diff --git a/spring-web/src/main/java/org/springframework/web/server/adapter/HttpWebHandlerAdapter.java b/spring-web/src/main/java/org/springframework/web/server/adapter/HttpWebHandlerAdapter.java index f8ac4cccac..cc593df745 100644 --- a/spring-web/src/main/java/org/springframework/web/server/adapter/HttpWebHandlerAdapter.java +++ b/spring-web/src/main/java/org/springframework/web/server/adapter/HttpWebHandlerAdapter.java @@ -16,7 +16,6 @@ package org.springframework.web.server.adapter; -import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import io.micrometer.observation.Observation; @@ -29,7 +28,6 @@ import reactor.core.publisher.Mono; import reactor.util.context.Context; import org.springframework.context.ApplicationContext; -import org.springframework.core.NestedExceptionUtils; import org.springframework.core.log.LogFormatUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -54,6 +52,7 @@ import org.springframework.web.server.i18n.AcceptHeaderLocaleContextResolver; import org.springframework.web.server.i18n.LocaleContextResolver; import org.springframework.web.server.session.DefaultWebSessionManager; import org.springframework.web.server.session.WebSessionManager; +import org.springframework.web.util.DisconnectedClientHelper; /** * Default adapter of {@link WebHandler} to the {@link HttpHandler} contract. @@ -69,20 +68,14 @@ import org.springframework.web.server.session.WebSessionManager; public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHandler { /** - * Dedicated log category for disconnected client exceptions. - *
Servlet containers don't expose a client disconnected callback; see - * eclipse-ee4j/servlet-api#44. - *
To avoid filling logs with unnecessary stack traces, we make an
- * effort to identify such network failures on a per-server basis, and then
- * log under a separate log category a simple one-line message at DEBUG level
- * or a full stack trace only at TRACE level.
+ * Log category to use for network failure after a client has gone away.
+ * @see DisconnectedClientHelper
*/
private static final String DISCONNECTED_CLIENT_LOG_CATEGORY =
"org.springframework.web.server.DisconnectedClient";
- // Similar declaration exists in AbstractSockJsSession.
- private static final Set Servlet containers don't expose a client disconnected callback; see
- * eclipse-ee4j/servlet-api#44.
- * Therefore, network IO failures may occur simply because a client has gone away,
- * and that can fill the logs with unnecessary stack traces.
- * We make a best effort to identify such network failures, on a per-server
- * basis, and log them under a separate log category. A simple one-line message
- * is logged at DEBUG level, while a full stack trace is shown at TRACE level.
- * @see #disconnectedClientLogger
+ * Log category to use for network failure after a client has gone away.
+ * @see DisconnectedClientHelper
*/
public static final String DISCONNECTED_CLIENT_LOG_CATEGORY =
"org.springframework.web.socket.sockjs.DisconnectedClient";
- /**
- * Tomcat: ClientAbortException or EOFException
- * Jetty: EofException
- * WildFly, GlassFish: java.io.IOException "Broken pipe" (already covered)
- * TODO:
- * This definition is currently duplicated between HttpWebHandlerAdapter
- * and AbstractSockJsSession. It is a candidate for a common utility class.
- * @see #indicatesDisconnectedClient(Throwable)
- */
- private static final Set
+ *
+ */
+ public boolean isClientDisconnectedException(Throwable ex) {
+ String message = NestedExceptionUtils.getMostSpecificCause(ex).getMessage();
+ if (message != null) {
+ String text = message.toLowerCase();
+ for (String phrase : EXCEPTION_PHRASES) {
+ if (text.contains(phrase)) {
+ return true;
+ }
+ }
+ }
+ return EXCEPTION_TYPE_NAMES.contains(ex.getClass().getSimpleName());
+ }
+
+ /**
+ * Check via {@link #isClientDisconnectedException} if the exception
+ * indicates the remote client disconnected, and if so log a single line
+ * message when DEBUG is on, and a full stacktrace when TRACE is on for
+ * the configured logger.
+ */
+ public boolean checkAndLogClientDisconnectedException(Throwable ex) {
+ if (isClientDisconnectedException(ex)) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Looks like the client has gone away", ex);
+ }
+ else if (logger.isDebugEnabled()) {
+ logger.debug("Looks like the client has gone away: " + ex +
+ " (For a full stack trace, set the log category '" + logger + "' to TRACE level.)");
+ }
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java
index cddfe1f41c..583a25616d 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 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.
@@ -23,14 +23,12 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.springframework.core.NestedExceptionUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.socket.CloseStatus;
@@ -43,6 +41,7 @@ import org.springframework.web.socket.sockjs.frame.SockJsFrame;
import org.springframework.web.socket.sockjs.frame.SockJsMessageCodec;
import org.springframework.web.socket.sockjs.transport.SockJsServiceConfig;
import org.springframework.web.socket.sockjs.transport.SockJsSession;
+import org.springframework.web.util.DisconnectedClientHelper;
/**
* An abstract base class for SockJS sessions implementing {@link SockJsSession}.
@@ -57,37 +56,15 @@ public abstract class AbstractSockJsSession implements SockJsSession {
/**
- * Log category to use on network IO exceptions after a client has gone away.
- *