This is an automated email from the ASF dual-hosted git repository.

rmaucher pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit ca01929c2d9f8c7bf5d7e66d06872c094a606ac2
Author: opencode <[email protected]>
AuthorDate: Sat Oct 17 13:50:48 2026 +0200

    Reject @OnMessage methods with a primitive payload and boolean last flag
    
    Partial message delivery is only defined for String, byte[] and
    ByteBuffer payloads, optionally with a boolean last flag. Tomcat
    already rejects Reader, InputStream, PongMessage and decoded object
    payloads combined with a boolean last flag at deployment time but did
    not reject the same combination for a primitive (or boxed primitive)
    payload parameter.
    
    For a method like void onMessage(int payload, boolean last) the method
    mapping was accepted, handler creation then fell through to the partial
    ByteBuffer branch and constructed PojoMessageHandlerPartialBinary with
    an index of -1. The first fragmented binary message caused
    parameters[indexPayload] = message in
    PojoMessageHandlerPartialBase.onMessage() to throw an
    ArrayIndexOutOfBoundsException outside the try/catch, which WsFrameBase
    handled as an unexpected error, invoking onError() and closing the
    session abnormally (1006). Any client connecting to such an endpoint
    could trigger this with a single fragmented binary message.
    
    Reject the parameter combination at deployment time instead, matching
    the existing partial parameter validation, with a new
    pojoMethodMapping.partialPrimitive message. This also removes the last
    route by which the partial message handler creation could be reached
    without a valid payload index.
    
    Add a regression test asserting the DeploymentException is raised for
    this signature.
---
 .../tomcat/websocket/pojo/LocalStrings.properties  |  1 +
 .../tomcat/websocket/pojo/PojoMethodMapping.java   |  5 ++++
 .../websocket/pojo/TestPojoMethodMapping.java      | 27 ++++++++++++++++++++++
 3 files changed, 33 insertions(+)

diff --git a/java/org/apache/tomcat/websocket/pojo/LocalStrings.properties 
b/java/org/apache/tomcat/websocket/pojo/LocalStrings.properties
index 9945db9ca7..165b4f2bad 100644
--- a/java/org/apache/tomcat/websocket/pojo/LocalStrings.properties
+++ b/java/org/apache/tomcat/websocket/pojo/LocalStrings.properties
@@ -46,6 +46,7 @@ pojoMethodMapping.paramWithoutAnnotation=A parameter of type 
[{0}] was found on
 pojoMethodMapping.partialInputStream=Invalid InputStream and boolean 
parameters present on the method [{0}] of class [{1}] that was annotated with 
OnMessage
 pojoMethodMapping.partialObject=Invalid Object and boolean parameters present 
on the method [{0}] of class [{1}] that was annotated with OnMessage
 pojoMethodMapping.partialPong=Invalid PongMessage and boolean parameters 
present on the method [{0}] of class [{1}] that was annotated with OnMessage
+pojoMethodMapping.partialPrimitive=Invalid primitive type and boolean 
parameters present on the method [{0}] of class [{1}] that was annotated with 
OnMessage
 pojoMethodMapping.partialReader=Invalid Reader and boolean parameters present 
on the method [{0}] of class [{1}] that was annotated with OnMessage
 pojoMethodMapping.pongWithPayload=Invalid PongMessage and Message parameters 
present on the method [{0}] of class [{1}] that was annotated with OnMessage
 
diff --git a/java/org/apache/tomcat/websocket/pojo/PojoMethodMapping.java 
b/java/org/apache/tomcat/websocket/pojo/PojoMethodMapping.java
index d369e5a060..10af09174b 100644
--- a/java/org/apache/tomcat/websocket/pojo/PojoMethodMapping.java
+++ b/java/org/apache/tomcat/websocket/pojo/PojoMethodMapping.java
@@ -626,6 +626,11 @@ public class PojoMethodMapping {
                 throw new 
DeploymentException(sm.getString("pojoMethodMapping.partialInputStream", 
m.getName(),
                         m.getDeclaringClass().getName()));
             }
+            if (indexPrimitive != -1 && indexBoolean != -1) {
+                throw new DeploymentException(
+                        sm.getString("pojoMethodMapping.partialPrimitive", 
m.getName(),
+                                m.getDeclaringClass().getName()));
+            }
             if (decoderMatch != null && decoderMatch.hasMatches() && 
indexBoolean != -1) {
                 throw new DeploymentException(
                         sm.getString("pojoMethodMapping.partialObject", 
m.getName(), m.getDeclaringClass().getName()));
diff --git a/test/org/apache/tomcat/websocket/pojo/TestPojoMethodMapping.java 
b/test/org/apache/tomcat/websocket/pojo/TestPojoMethodMapping.java
index 431ddc2925..9e402bcb69 100644
--- a/test/org/apache/tomcat/websocket/pojo/TestPojoMethodMapping.java
+++ b/test/org/apache/tomcat/websocket/pojo/TestPojoMethodMapping.java
@@ -21,6 +21,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import javax.websocket.ContainerProvider;
+import javax.websocket.DeploymentException;
 import javax.websocket.OnClose;
 import javax.websocket.OnMessage;
 import javax.websocket.OnOpen;
@@ -96,6 +97,32 @@ public class TestPojoMethodMapping extends TomcatBaseTest {
     }
 
 
+    /*
+     * Only String, byte[] and ByteBuffer payloads (plus the boolean last 
flag) are permitted for partial messages. A
+     * primitive payload combined with a boolean last flag must be rejected at 
deployment time rather than resulting in
+     * a partial message handler that fails when the first fragmented message 
arrives.
+     */
+    @Test
+    public void testPrimitiveAndBooleanParameters() throws Exception {
+        try {
+            new PojoMethodMapping(PrimitiveAndBooleanServer.class, new 
ArrayList<>(), "/primitiveAndBoolean", null);
+            Assert.fail("Expected a DeploymentException");
+        } catch (DeploymentException de) {
+            Assert.assertTrue("Unexpected message: " + de.getMessage(), 
de.getMessage().contains("primitive type"));
+        }
+    }
+
+
+    @ServerEndpoint("/primitiveAndBoolean")
+    public static final class PrimitiveAndBooleanServer {
+
+        @OnMessage
+        public void onMessage(@SuppressWarnings("unused") int payload, 
@SuppressWarnings("unused") boolean last) {
+            // No-Op
+        }
+    }
+
+
     @ServerEndpoint(value = "/{one}/{two}/{three}", configurator = 
SingletonConfigurator.class)
     public static final class Server {
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to