https://bz.apache.org/bugzilla/show_bug.cgi?id=65763
Bug ID: 65763
Summary: WsRemoteEndpointImplBase#sendMessageBlock cannot close
session properly when have TimeoutException
Product: Tomcat 9
Version: 9.0.44
Hardware: PC
OS: Mac OS X 10.1
Status: NEW
Severity: normal
Priority: P2
Component: WebSocket
Assignee: [email protected]
Reporter: [email protected]
Target Milestone: -----
Firstly, I found this exception:
####
java.io.IOException: java.util.concurrent.TimeoutException
at
org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendMessageBlock(WsRemoteEndpointImplBase.java:324)
at
org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendMessageBlock(WsRemoteEndpointImplBase.java:259)
at
####
It happens in
org.apache.tomcat.websocket.WsRemoteEndpointImplBase#sendMessageBlock(byte,
java.nio.ByteBuffer, boolean, long):
###java
if (!bsh.getSendResult().isOK()) {
messagePartInProgress.release();
Throwable t = bsh.getSendResult().getException();
wsSession.doClose(new CloseReason(CloseCodes.GOING_AWAY,
t.getMessage()),
new CloseReason(CloseCodes.CLOSED_ABNORMALLY,
t.getMessage()), true);
throw new IOException (t);
}
###
Actually, it is caused by
org.apache.tomcat.websocket.WsRemoteEndpointImplClient#doWrite which has a
LongToIntegerFuture.get timeout exception:
###java
try {
channel.write(byteBuffer).get(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException |
TimeoutException e) {
handler.onResult(new SendResult(e));
return;
}
###
But "writing" value is not set to "false" which has been set "true" in
org.apache.tomcat.websocket.AsyncChannelWrapperSecure#write(java.nio.ByteBuffer).
Only in org.apache.tomcat.websocket.AsyncChannelWrapperSecure.WriteTask,
"writing" will be reset to "false", but WriteTask may not finish when
LongToIntegerFuture.get timeout happens. This problem may like
https://bz.apache.org/bugzilla/show_bug.cgi?id=61003, but writing.set(false)
doesn't work in this timeout situation.
When WsRemoteEndpointImplBase#sendMessageBlock get TimeoutException, it will
try to close session, see "wsSession.doClose(new
CloseReason(CloseCodes.GOING_AWAY, t.getMessage())".
This method "wsSession.doClose" will set state = State.OUTPUT_CLOSED and send a
close message (org.apache.tomcat.websocket.WsSession#sendCloseMessage). But it
will case exception, because "writing" value is still "true":
###
Caused by: java.lang.IllegalStateException: Concurrent write operations are
not permitted
at
org.apache.tomcat.websocket.AsyncChannelWrapperSecure.write(AsyncChannelWrapperSecure.java:117)
at
org.apache.tomcat.websocket.WsRemoteEndpointImplClient.doWrite(WsRemoteEndpointImplClient.java:62)
at
org.apache.tomcat.websocket.WsRemoteEndpointImplBase$OutputBufferSendHandler.write(WsRemoteEndpointImplBase.java:893)
at
org.apache.tomcat.websocket.WsRemoteEndpointImplBase.writeMessagePart(WsRemoteEndpointImplBase.java:506)
at
org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendMessageBlock(WsRemoteEndpointImplBase.java:311)
at
org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendMessageBlock(WsRemoteEndpointImplBase.java:259)
at
org.apache.tomcat.websocket.WsSession.sendCloseMessage(WsSession.java:613)
at org.apache.tomcat.websocket.WsSession.doClose(WsSession.java:498)
at
org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendMessageBlock(WsRemoteEndpointImplBase.java:322)
###
In org.apache.tomcat.websocket.WsSession#sendCloseMessage, it doesn't catch
IllegalStateException and close websocket channel:
###java
try {
wsRemoteEndpoint.sendMessageBlock(Constants.OPCODE_CLOSE, msg,
true);
} catch (IOException | WritePendingException e) {
// Failed to send close message. Close the socket and let the
caller
// deal with the Exception
if (log.isDebugEnabled()) {
log.debug(sm.getString("wsSession.sendCloseFail", id), e);
}
wsRemoteEndpoint.close();
###
And this session may cannot be closed again because state has been set to
"OUTPUT_CLOSED".
I think IllegalStateException should be caught in
org.apache.tomcat.websocket.WsSession#sendCloseMessage or a better way to close
session when timeout happens.
--
You are receiving this mail because:
You are the assignee for the bug.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]