(tomcat) branch main updated: Follow-up to 06da787736

[email protected]
Newsgroups gmane.comp.jakarta.tomcat.devel
Message-ID <178670512071.345568.13934952185708372320@gitbox3-he-fi.apache.org>
This is an automated email from the ASF dual-hosted git repository.

markt-asf pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/main by this push:
     new f69ab9e9a6 Follow-up to 06da787736
f69ab9e9a6 is described below

commit f69ab9e9a66f197af59e2a1096a6907a131c38ee
Author: Mark Thomas <[email protected]>
AuthorDate: Fri Aug 14 11:58:34 2026 +0100

    Follow-up to 06da787736
---
 java/org/apache/tomcat/websocket/WsFrameBase.java  |  9 ++--
 .../tomcat/websocket/WsRemoteEndpointImplBase.java |  3 +-
 test/org/apache/tomcat/websocket/TestWsFrame.java  | 63 +++++++++++++++-------
 webapps/docs/changelog.xml                         |  9 ++--
 4 files changed, 56 insertions(+), 28 deletions(-)

diff --git a/java/org/apache/tomcat/websocket/WsFrameBase.java b/java/org/apache/tomcat/websocket/WsFrameBase.java
index a3d3633df6..4077c32ae8 100644
--- a/java/org/apache/tomcat/websocket/WsFrameBase.java
+++ b/java/org/apache/tomcat/websocket/WsFrameBase.java
@@ -16,6 +16,7 @@
  */
 package org.apache.tomcat.websocket;
 
+import java.io.EOFException;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
@@ -379,10 +380,12 @@ public abstract class WsFrameBase {
             if (wsSession.isOpen()) {
                 try {
                     wsSession.getBasicRemote().sendPong(controlBufferBinary);
-                } catch (IllegalStateException ise) {
-                    // The close process may have started after isOpen() was checked.
+                } catch (IllegalStateException | EOFException e) {
+                    // IllegalStateException - wsSession started to close while pong was being prepared
+                    // EOFException - wsSession closed while pong was being prepared
+                    // Either way, ignore the error
                     if (!wsSession.isClosing()) {
-                        throw ise;
+                        throw e;
                     }
                 }
             }
diff --git a/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java b/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java
index 9d5f4d39c0..deb80519fd 100644
--- a/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java
+++ b/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java
@@ -16,6 +16,7 @@
  */
 package org.apache.tomcat.websocket;
 
+import java.io.EOFException;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.Writer;
@@ -548,7 +549,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
 
     void writeMessagePart(MessagePart mp) throws IOException {
         if (closed) {
-            throw new IOException(sm.getString("wsRemoteEndpoint.closed"));
+            throw new EOFException(sm.getString("wsRemoteEndpoint.closed"));
         }
 
         if (Constants.INTERNAL_OPCODE_FLUSH == mp.getOpCode()) {
diff --git a/test/org/apache/tomcat/websocket/TestWsFrame.java b/test/org/apache/tomcat/websocket/TestWsFrame.java
index 2096fbbf76..503d384728 100644
--- a/test/org/apache/tomcat/websocket/TestWsFrame.java
+++ b/test/org/apache/tomcat/websocket/TestWsFrame.java
@@ -16,17 +16,18 @@
  */
 package org.apache.tomcat.websocket;
 
+import java.io.EOFException;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 
 import jakarta.websocket.RemoteEndpoint;
 
-import org.easymock.EasyMock;
 import org.junit.Assert;
 import org.junit.Test;
 
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
+import org.easymock.EasyMock;
 
 public class TestWsFrame {
 
@@ -69,39 +70,61 @@ public class TestWsFrame {
 
     @Test
     public void testAutomaticPongAfterCloseStarted() throws Exception {
-        WsSession wsSession = EasyMock.createNiceMock(WsSession.class);
-        RemoteEndpoint.Basic basicRemote = EasyMock.createMock(RemoteEndpoint.Basic.class);
-        EasyMock.expect(wsSession.isOpen()).andReturn(Boolean.TRUE);
-        EasyMock.expect(wsSession.getBasicRemote()).andReturn(basicRemote);
-        basicRemote.sendPong(EasyMock.anyObject(ByteBuffer.class));
-        EasyMock.expectLastCall().andThrow(new IllegalStateException());
-        EasyMock.expect(wsSession.isClosing()).andReturn(Boolean.TRUE);
-        EasyMock.replay(wsSession, basicRemote);
+        doTestAutomaticPongFailure(new IllegalStateException(), true, true);
+    }
 
-        TestFrame frame = new TestFrame(wsSession);
-        frame.processPing();
 
-        EasyMock.verify(wsSession, basicRemote);
+    @Test
+    public void testAutomaticPongISEWhileOpen() throws Exception {
+        doTestAutomaticPongFailure(new IllegalStateException(), false, false);
+    }
+
+
+    @Test
+    public void testAutomaticPongAfterCloseCompleted() throws Exception {
+        doTestAutomaticPongFailure(new EOFException(), true, true);
     }
 
 
     @Test
-    public void testAutomaticPongFailureWhileOpen() throws Exception {
+    public void testAutomaticPongEOFWhileOpen() throws Exception {
+        doTestAutomaticPongFailure(new EOFException(), false, false);
+    }
+
+
+    @Test
+    public void testAutomaticPongIOEAfterCloseStarted() throws Exception {
+        doTestAutomaticPongFailure(new IOException(), true, false);
+    }
+
+
+    @Test
+    public void testAutomaticPongIOEWhileOpen() throws Exception {
+        doTestAutomaticPongFailure(new IOException(), false, false);
+    }
+
+
+    private static void doTestAutomaticPongFailure(Exception failure, boolean closing, boolean swallowed)
+            throws Exception {
         WsSession wsSession = EasyMock.createNiceMock(WsSession.class);
         RemoteEndpoint.Basic basicRemote = EasyMock.createMock(RemoteEndpoint.Basic.class);
-        EasyMock.expect(wsSession.isOpen()).andReturn(Boolean.TRUE);
+        EasyMock.expect(Boolean.valueOf(wsSession.isOpen())).andReturn(Boolean.TRUE);
         EasyMock.expect(wsSession.getBasicRemote()).andReturn(basicRemote);
         basicRemote.sendPong(EasyMock.anyObject(ByteBuffer.class));
-        EasyMock.expectLastCall().andThrow(new IllegalStateException());
-        EasyMock.expect(wsSession.isClosing()).andReturn(Boolean.FALSE);
+        EasyMock.expectLastCall().andThrow(failure);
+        EasyMock.expect(Boolean.valueOf(wsSession.isClosing())).andStubReturn(Boolean.valueOf(closing));
         EasyMock.replay(wsSession, basicRemote);
 
         TestFrame frame = new TestFrame(wsSession);
-        try {
+        if (swallowed) {
             frame.processPing();
-            Assert.fail();
-        } catch (IllegalStateException expected) {
-            // Expected.
+        } else {
+            try {
+                frame.processPing();
+                Assert.fail();
+            } catch (Exception actual) {
+                Assert.assertSame(failure, actual);
+            }
         }
 
         EasyMock.verify(wsSession, basicRemote);
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index bb64015f0e..6d8e0727aa 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -336,10 +336,6 @@
   </subsection>
   <subsection name="WebSocket">
     <changelog>
-      <fix>
-        Fix an exception when an automatic Pong response races with the
-        closing of the WebSocket session. (moritzfl)
-      </fix>
       <update>
         Update Tomcat's WebSocket support to version 2.3 of the Jakarta
         WebSocket API. (markt)
@@ -367,6 +363,11 @@
         <code>Writer</code> and <code>OutputStream</code>. (markt)
       </fix>
       <!-- Entries for backport and removal before 12.0.0-M1 below this line -->
+      <fix>
+        Fix an exception when an automatic Pong response races with the
+        closing of the WebSocket session. Pull request <pr>1041</pr> provided by
+        moritzfl. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Web applications">
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.