diff options
author | Simon Rettberg | 2020-07-20 17:01:17 +0200 |
---|---|---|
committer | Simon Rettberg | 2020-07-20 17:01:17 +0200 |
commit | a39496a125170163a93c557547156f7073c0e532 (patch) | |
tree | 52f21e0458243bbabe739630e5ada1d9e8e876ae | |
parent | Fix off-by-one when displaying tunnel count on close event (diff) | |
download | bwlp-guacamole-ext-a39496a125170163a93c557547156f7073c0e532.tar.gz bwlp-guacamole-ext-a39496a125170163a93c557547156f7073c0e532.tar.xz bwlp-guacamole-ext-a39496a125170163a93c557547156f7073c0e532.zip |
Refine timeout handling of dead/removed clients
A client that was just claimed gets a one minute grace period before the
"no active tunnel detection" can kick in. This prevents a race where the
client gets claimed and then a json update is performed before the
tunnel connection is established.
Also the removal of clients that are missing from the remote meta data
is hopefully more robust now, not removing a client as long as there is
an active user and an open tunnel connection.
-rw-r--r-- | src/main/java/de/bwlehrpool/bwlp_guac/AvailableClient.java | 49 |
1 files changed, 36 insertions, 13 deletions
diff --git a/src/main/java/de/bwlehrpool/bwlp_guac/AvailableClient.java b/src/main/java/de/bwlehrpool/bwlp_guac/AvailableClient.java index 5371547..aa7506d 100644 --- a/src/main/java/de/bwlehrpool/bwlp_guac/AvailableClient.java +++ b/src/main/java/de/bwlehrpool/bwlp_guac/AvailableClient.java @@ -36,7 +36,15 @@ public class AvailableClient implements Cloneable { private WrappedConnection connection; + /** + * TS when to delete this entry, or 0 = never + */ private long deadline; + + /** + * TA when to release this client if it's claimed, but state is not occupied + */ + private long idleClaimTimeout; private long lastConnectionCheck; @@ -60,9 +68,9 @@ public class AvailableClient implements Cloneable { * can't possibly be in use by the old user anymore. */ public synchronized void update(JsonClient source) { - if (this.inUseBy != null && source.state != State.OCCUPIED && !TunnelListener.hasTunnel(this.inUseBy)) { - LOGGER.info("Free client blocked by a disconnected user detected."); - LOGGER.info("Client " + this + " is available again"); + if (this.inUseBy != null && source.state != State.OCCUPIED && !TunnelListener.hasTunnel(this.inUseBy) + && System.currentTimeMillis() > this.idleClaimTimeout) { + LOGGER.info("Client " + this + " is available again (free client blocked by a user)"); this.inUseBy = null; if (this.connection != null) { this.connection.invalidate(); @@ -70,19 +78,20 @@ public class AvailableClient implements Cloneable { } if (this.password == null || !this.password.equals(source.password)) { - if (source.state != State.OCCUPIED) { - if (this.inUseBy != null) { - LOGGER.info("Client " + this + " is available again"); - this.inUseBy = null; - if (this.connection != null) this.connection.invalidate(); + if (source.state != State.OCCUPIED && this.inUseBy != null) { + LOGGER.info("Client " + this + " is available again"); + this.inUseBy = null; + if (this.connection != null) { + this.connection.invalidate(); } } this.lastConnectionCheck = 0; this.password = source.password; } - if (this.inUseBy == null || source.state != State.IDLE) + if (this.inUseBy == null || source.state != State.IDLE) { this.state = source.state; + } this.deadline = 0; } @@ -95,6 +104,7 @@ public class AvailableClient implements Cloneable { return false; this.inUseBy = user; this.state = State.OCCUPIED; + this.idleClaimTimeout = System.currentTimeMillis() + 60000; // Give connect and login some time return true; } @@ -113,7 +123,9 @@ public class AvailableClient implements Cloneable { public synchronized void releaseConnection(String expectedOwner) { if (isInUseBy(expectedOwner)) { - if (this.connection != null) this.connection.invalidate(); + if (this.connection != null) { + this.connection.invalidate(); + } LOGGER.info("Prematurely releasing client " + this); this.inUseBy = null; } else { @@ -123,14 +135,25 @@ public class AvailableClient implements Cloneable { /** * If this connection is not returned by the sat server anymore, we keep it - * around for another 5 minutes just in case. + * around as long as there is someone using it, with some safety buffer. */ public boolean isTimeout(long NOW) { if (deadline == 0) { - deadline = NOW + 300000; + deadline = NOW + 120000; // Was recently updated from remote, set 2min timeout + return false; + } + if (NOW < deadline) // Not timed out yet, no need to recheck return false; + long remaining = deadline - NOW; + if (remaining < 120000) { + synchronized (this) { + if (this.inUseBy != null && TunnelListener.hasTunnel(this.inUseBy)) { + deadline = NOW + 600000; // Another 10 minutes since the tunnel is active + LOGGER.info("Extending deadline of vanished client, because it's in use: " + this); + } + } } - return deadline < NOW; + return NOW > deadline; } @Override |