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: dev@tomcat.apache.org
          Reporter: qintai....@oceanbase.com
  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: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to