Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for passive connection lost checking #1290

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
53 changes: 50 additions & 3 deletions src/main/java/org/java_websocket/AbstractWebSocket.java
Expand Up @@ -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;
Expand Down Expand Up @@ -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
*
Expand All @@ -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
Expand All @@ -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();
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
}
}

}
9 changes: 8 additions & 1 deletion src/main/java/org/java_websocket/WebSocketImpl.java
Expand Up @@ -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);
}

Expand All @@ -451,6 +455,9 @@ private void closeConnectionDueToInternalServerError(RuntimeException exception)
private ByteBuffer generateHttpResponseDueToError(int errorCode) {
String errorCodeDescription;
switch (errorCode) {
case 401:
errorCodeDescription = "401 Authorization Required";
break;
case 404:
errorCodeDescription = "404 WebSocket Upgrade Failure";
break;
Expand Down
Expand Up @@ -40,6 +40,8 @@ public class InvalidDataException extends Exception {
*/
private final int closecode;

private Integer httpErrorCode;

/**
* constructor for a InvalidDataException
*
Expand All @@ -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.
*
Expand All @@ -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.
*
Expand Down Expand Up @@ -92,4 +118,13 @@ public int getCloseCode() {
return closecode;
}

/**
* Getter httpErrorCode
*
* @return the httpErrorCode
*/
public Integer getHttpErrorCode() {
return httpErrorCode;
}

}