Details
-
Bug
-
Status: Resolved
-
Blocker
-
Resolution: Fixed
-
None
-
None
Description
If Tomcat (or another servlet container) encounters a hard, internal I/O error while handling a WebSocket connection, the resulting IOException will cause the connection thread to die:
15:39:50.882 [Thread-19] DEBUG o.a.g.w.GuacamoleWebSocketTunnelEndpoint - I/O error prevents further reads. java.io.IOException: java.util.concurrent.ExecutionException: java.io.IOException: Key must be cancelled at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendPartialString(WsRemoteEndpointImplBase.java:228) ~[tomcat-websocket.jar:8.0.14] at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendString(WsRemoteEndpointImplBase.java:172) ~[tomcat-websocket.jar:8.0.14] at org.apache.tomcat.websocket.WsRemoteEndpointBasic.sendText(WsRemoteEndpointBasic.java:37) ~[tomcat-websocket.jar:8.0.14] at org.apache.guacamole.websocket.GuacamoleWebSocketTunnelEndpoint$2.run(GuacamoleWebSocketTunnelEndpoint.java:169) ~[GuacamoleWebSocketTunnelEndpoint$2.class:na] Caused by: java.util.concurrent.ExecutionException: java.io.IOException: Key must be cancelled at org.apache.tomcat.websocket.FutureToSendHandler.get(FutureToSendHandler.java:102) ~[tomcat-websocket.jar:8.0.14] at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendPartialString(WsRemoteEndpointImplBase.java:224) ~[tomcat-websocket.jar:8.0.14] ... 3 common frames omitted Caused by: java.io.IOException: Key must be cancelled at org.apache.coyote.http11.upgrade.NioServletOutputStream.doWriteInternal(NioServletOutputStream.java:84) ~[tomcat-coyote.jar:8.0.14] at org.apache.coyote.http11.upgrade.NioServletOutputStream.doWrite(NioServletOutputStream.java:61) ~[tomcat-coyote.jar:8.0.14] at org.apache.coyote.http11.upgrade.AbstractServletOutputStream.writeInternal(AbstractServletOutputStream.java:162) ~[tomcat-coyote.jar:8.0.14] at org.apache.coyote.http11.upgrade.AbstractServletOutputStream.write(AbstractServletOutputStream.java:129) ~[tomcat-coyote.jar:8.0.14] at org.apache.tomcat.websocket.server.WsRemoteEndpointImplServer.onWritePossible(WsRemoteEndpointImplServer.java:99) ~[tomcat-websocket.jar:8.0.14] at org.apache.tomcat.websocket.server.WsRemoteEndpointImplServer.doWrite(WsRemoteEndpointImplServer.java:81) ~[tomcat-websocket.jar:8.0.14] at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.writeMessagePart(WsRemoteEndpointImplBase.java:420) ~[tomcat-websocket.jar:8.0.14] at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.startMessage(WsRemoteEndpointImplBase.java:311) ~[tomcat-websocket.jar:8.0.14] at org.apache.tomcat.websocket.WsRemoteEndpointImplBase$TextMessageSendHandler.write(WsRemoteEndpointImplBase.java:675) ~[tomcat-websocket.jar:8.0.14] at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendPartialString(WsRemoteEndpointImplBase.java:220) ~[tomcat-websocket.jar:8.0.14] ... 3 common frames omitted
This death is fine, but the code handling that error does not properly invoke closeConnection(), thus the associated GuacamoleTunnel and GuacamoleSocket are not guaranteed to be closed. They will eventually close due to timeout, but the lack of further read errors means that internal connection tracking will never realize the connection is closed. Extensions like guacamole-auth-jdbc which track connection activity based on closure will fail to properly record the connection as dead, and will require that the connection be explicitly closed by an admin.