From f66c20979880a48453293b23aeb01d362f150a45 Mon Sep 17 00:00:00 2001 From: Robert Schlabbach Date: Mon, 10 Oct 2022 09:19:46 +0200 Subject: [PATCH 1/3] Add support for passive connection lost checking When both ends of a WebSocket connection need to know when the connection is lost, both sending pings is redundant, as a received ping already indicates that the peer is still connected. Add an optional boolean parameter to the setConnectionLostTimeout() method, which when set to true makes the connection lost cheking passive, i.e. the check never sends out pings, but only checks whether an incoming ping was received recently enough. For passive connection lost checking, the peer must be known to do active connection lost checking with a timeout equal (or lower) than the one locally used. --- .../org/java_websocket/AbstractWebSocket.java | 53 +++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/java_websocket/AbstractWebSocket.java b/src/main/java/org/java_websocket/AbstractWebSocket.java index c3e77a089..f5f20a82e 100644 --- a/src/main/java/org/java_websocket/AbstractWebSocket.java +++ b/src/main/java/org/java_websocket/AbstractWebSocket.java @@ -32,6 +32,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.java_websocket.framing.CloseFrame; +import org.java_websocket.framing.Framedata; import org.java_websocket.util.NamedThreadFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -83,6 +84,13 @@ public abstract class AbstractWebSocket extends WebSocketAdapter { */ private long connectionLostTimeout = TimeUnit.SECONDS.toNanos(60); + /** + * Attribute for the passive lost connection check + * + * @since 1.5.5 + */ + private boolean connectionLostCheckPassive = false; + /** * Attribute to keep track if the WebSocket Server/Client is running/connected * @@ -107,6 +115,16 @@ public int getConnectionLostTimeout() { } } + /** + * Tests if connection lost check is passive + * + * @return a boolean indicating whether or not the connection lost check is passive + * @since 1.5.5 + */ + public boolean isConnectionLostCheckPassive() { + return connectionLostCheckPassive; + } + /** * Setter for the interval checking for lost connections A value lower or equal 0 results in the * check to be deactivated @@ -115,8 +133,21 @@ public int getConnectionLostTimeout() { * @since 1.3.4 */ public void setConnectionLostTimeout(int connectionLostTimeout) { + setConnectionLostTimeout(connectionLostTimeout, false); + } + + /** + * Setter for the interval checking for lost connections A value lower or equal 0 results in the + * check to be deactivated + * + * @param connectionLostTimeout the interval in seconds + * @param passive true for passive lost connection checks, false for active checks + * @since 1.5.5 + */ + public void setConnectionLostTimeout(int connectionLostTimeout, boolean passive) { synchronized (syncConnectionLost) { this.connectionLostTimeout = TimeUnit.SECONDS.toNanos(connectionLostTimeout); + this.connectionLostCheckPassive = passive; if (this.connectionLostTimeout <= 0) { log.trace("Connection lost timer stopped"); cancelConnectionLostTimer(); @@ -228,11 +259,12 @@ private void executeConnectionLostDetection(WebSocket webSocket, long minimumPon } WebSocketImpl webSocketImpl = (WebSocketImpl) webSocket; if (webSocketImpl.getLastPong() < minimumPongTime) { - log.trace("Closing connection due to no pong received: {}", webSocketImpl); + log.trace("Closing connection due to no {} received: {}", connectionLostCheckPassive ? "ping" : "pong", webSocketImpl); webSocketImpl.closeConnection(CloseFrame.ABNORMAL_CLOSE, - "The connection was closed because the other endpoint did not respond with a pong in time. For more information check: https://github.com/TooTallNate/Java-WebSocket/wiki/Lost-connection-detection"); + "The connection was closed because the other endpoint did not " + (connectionLostCheckPassive ? "send a ping" : "respond with a pong") + " in time. " + + "For more information check: https://github.com/TooTallNate/Java-WebSocket/wiki/Lost-connection-detection"); } else { - if (webSocketImpl.isOpen()) { + if (webSocketImpl.isOpen() && !connectionLostCheckPassive) { webSocketImpl.sendPing(); } else { log.trace("Trying to ping a non open connection: {}", webSocketImpl); @@ -308,4 +340,19 @@ public void setReuseAddr(boolean reuseAddr) { this.reuseAddr = reuseAddr; } + /** + * This overriden implementation will additionally update the last pong time in case + * connection lost checks are passive. + * + * @see org.java_websocket.WebSocketListener#onWebsocketPing(WebSocket, Framedata) + */ + @Override + public void onWebsocketPing(WebSocket conn, Framedata f) { + super.onWebsocketPing(conn, f); + if (connectionLostCheckPassive && conn instanceof WebSocketImpl) { + WebSocketImpl webSocketImpl = (WebSocketImpl) conn; + webSocketImpl.updateLastPong(); + } + } + } From 9b17b7482bf1a97cbe1d63a4d934410aec08ea3d Mon Sep 17 00:00:00 2001 From: "paula.zachacz" Date: Tue, 28 Nov 2023 15:22:19 +0100 Subject: [PATCH 2/3] Support other http error codes --- .../org/java_websocket/WebSocketImpl.java | 9 ++++- .../exceptions/InvalidDataException.java | 35 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/java_websocket/WebSocketImpl.java b/src/main/java/org/java_websocket/WebSocketImpl.java index aad172127..478016c6c 100644 --- a/src/main/java/org/java_websocket/WebSocketImpl.java +++ b/src/main/java/org/java_websocket/WebSocketImpl.java @@ -428,7 +428,11 @@ private void decodeFrames(ByteBuffer socketBuffer) { * @param exception the InvalidDataException causing this problem */ private void closeConnectionDueToWrongHandshake(InvalidDataException exception) { - write(generateHttpResponseDueToError(404)); + if (exception.getHttpErrorCode() != null) { + write(generateHttpResponseDueToError(exception.getHttpErrorCode())); + } else { + write(generateHttpResponseDueToError(404)); + } flushAndClose(exception.getCloseCode(), exception.getMessage(), false); } @@ -451,6 +455,9 @@ private void closeConnectionDueToInternalServerError(RuntimeException exception) private ByteBuffer generateHttpResponseDueToError(int errorCode) { String errorCodeDescription; switch (errorCode) { + case 401: + errorCodeDescription = "401 WebSocket Authentication Failure"; + break; case 404: errorCodeDescription = "404 WebSocket Upgrade Failure"; break; diff --git a/src/main/java/org/java_websocket/exceptions/InvalidDataException.java b/src/main/java/org/java_websocket/exceptions/InvalidDataException.java index c34c8c941..3419d4b40 100644 --- a/src/main/java/org/java_websocket/exceptions/InvalidDataException.java +++ b/src/main/java/org/java_websocket/exceptions/InvalidDataException.java @@ -40,6 +40,8 @@ public class InvalidDataException extends Exception { */ private final int closecode; + private Integer httpErrorCode; + /** * constructor for a InvalidDataException * @@ -49,6 +51,17 @@ public InvalidDataException(int closecode) { this.closecode = closecode; } + /** + * constructor for a InvalidDataException + * + * @param closecode the closecode which will be returned + * @param httpErrorCode the httpErrorCode which will be returned. + */ + public InvalidDataException(int closecode, int httpErrorCode) { + this.closecode = closecode; + this.httpErrorCode = httpErrorCode; + } + /** * constructor for a InvalidDataException. * @@ -60,6 +73,19 @@ public InvalidDataException(int closecode, String s) { this.closecode = closecode; } + /** + * constructor for a InvalidDataException. + * + * @param closecode the closecode which will be returned. + * @param s the detail message. + * @param httpErrorCode the httpErrorCode which will be returned. + */ + public InvalidDataException(int closecode, String s, int httpErrorCode) { + super(s); + this.closecode = closecode; + this.httpErrorCode = httpErrorCode; + } + /** * constructor for a InvalidDataException. * @@ -92,4 +118,13 @@ public int getCloseCode() { return closecode; } + /** + * Getter httpErrorCode + * + * @return the httpErrorCode + */ + public Integer getHttpErrorCode() { + return httpErrorCode; + } + } From 6d3c69b7422766742c67b6492131739a55532df0 Mon Sep 17 00:00:00 2001 From: "paula.zachacz" Date: Tue, 28 Nov 2023 17:32:58 +0100 Subject: [PATCH 3/3] update error description on comment --- src/main/java/org/java_websocket/WebSocketImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/java_websocket/WebSocketImpl.java b/src/main/java/org/java_websocket/WebSocketImpl.java index 478016c6c..37523859c 100644 --- a/src/main/java/org/java_websocket/WebSocketImpl.java +++ b/src/main/java/org/java_websocket/WebSocketImpl.java @@ -456,7 +456,7 @@ private ByteBuffer generateHttpResponseDueToError(int errorCode) { String errorCodeDescription; switch (errorCode) { case 401: - errorCodeDescription = "401 WebSocket Authentication Failure"; + errorCodeDescription = "401 Authorization Required"; break; case 404: errorCodeDescription = "404 WebSocket Upgrade Failure";