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]
