diff --git a/airsonic-main/src/main/java/org/airsonic/player/controller/StreamController.java b/airsonic-main/src/main/java/org/airsonic/player/controller/StreamController.java index f804dd78..83f0e6c7 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/controller/StreamController.java +++ b/airsonic-main/src/main/java/org/airsonic/player/controller/StreamController.java @@ -29,7 +29,6 @@ import org.airsonic.player.service.sonos.SonosHelper; import org.airsonic.player.util.HttpRange; import org.airsonic.player.util.StringUtil; import org.airsonic.player.util.Util; -import org.apache.catalina.connector.ClientAbortException; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -249,9 +248,22 @@ public class StreamController { } } } - } catch (ClientAbortException e) { - LOG.info("{}: Client unexpectedly closed connection while loading {} ({})", request.getRemoteAddr(), Util.getURLForRequest(request), e.getCause().toString()); - return; + } catch (IOException e) { + + // This happens often and outside of the control of the server, so + // we catch Tomcat/Jetty "connection aborted by client" exceptions + // and display a short error message. + boolean shouldCatch = false; + shouldCatch |= Util.isInstanceOfClassName(e, "org.apache.catalina.connector.ClientAbortException"); + shouldCatch |= Util.isInstanceOfClassName(e, "org.eclipse.jetty.io.EofException"); + if (shouldCatch) { + LOG.info("{}: Client unexpectedly closed connection while loading {} ({})", request.getRemoteAddr(), Util.getURLForRequest(request), e.getCause().toString()); + return; + } + + // Rethrow the exception in all other cases + throw e; + } finally { if (status != null) { securityService.updateUserByteCounts(user, status.getBytesTransfered(), 0L, 0L); diff --git a/airsonic-main/src/main/java/org/airsonic/player/spring/LoggingExceptionResolver.java b/airsonic-main/src/main/java/org/airsonic/player/spring/LoggingExceptionResolver.java index 6ee5b55a..0dcb193d 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/spring/LoggingExceptionResolver.java +++ b/airsonic-main/src/main/java/org/airsonic/player/spring/LoggingExceptionResolver.java @@ -18,11 +18,19 @@ public class LoggingExceptionResolver implements HandlerExceptionResolver, Order public ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, Object o, Exception e ) { - if (e instanceof org.apache.catalina.connector.ClientAbortException) { + // This happens often and outside of the control of the server, so + // we catch Tomcat/Jetty "connection aborted by client" exceptions + // and display a short error message. + boolean shouldCatch = false; + shouldCatch |= Util.isInstanceOfClassName(e, "org.apache.catalina.connector.ClientAbortException"); + shouldCatch |= Util.isInstanceOfClassName(e, "org.eclipse.jetty.io.EofException"); + if (shouldCatch) { LOG.info("{}: Client unexpectedly closed connection while loading {} ({})", request.getRemoteAddr(), Util.getURLForRequest(request), e.getCause().toString()); - } else { - LOG.error("{}: An exception occurred while loading {}", request.getRemoteAddr(), Util.getURLForRequest(request), e); + return null; } + + // Display a full stack trace in all other cases + LOG.error("{}: An exception occurred while loading {}", request.getRemoteAddr(), Util.getURLForRequest(request), e); return null; } diff --git a/airsonic-main/src/main/java/org/airsonic/player/util/Util.java b/airsonic-main/src/main/java/org/airsonic/player/util/Util.java index b5c07da9..0a758f03 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/util/Util.java +++ b/airsonic-main/src/main/java/org/airsonic/player/util/Util.java @@ -132,4 +132,16 @@ public final class Util { if (queryString != null && queryString.length() > 0) url += "?" + queryString; return url; } + + /** + * Return true if the given object is an instance of the class name in argument. + * If the class doesn't exist, returns false. + */ + public static boolean isInstanceOfClassName(Object o, String className) { + try { + return Class.forName(className).isInstance(o); + } catch (ClassNotFoundException e) { + return false; + } + } }