This is an automated email from the ASF dual-hosted git repository.
Cole-Greer pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/master by this push:
new 3e0641a322 CTR Fix ClassCastException when numeric request fields are
deserialized as Integer
3e0641a322 is described below
commit 3e0641a322d402a0a32c9e1be068e054b7f184a4
Author: Cole Greer <[email protected]>
AuthorDate: Fri May 1 17:35:58 2026 -0700
CTR Fix ClassCastException when numeric request fields are deserialized as
Integer
Clients like gremlin-javascript serialize INT32-range values (e.g.
timeoutMs=5000) as GraphBinary INT (4 bytes), which deserializes to a
Java Integer. The direct (long) and (int) casts on boxed Integer/Long
threw ClassCastException, returning a 400 Bad Request.
Cast through Number instead so both Integer and Long inputs are handled
in RequestMessageSerializer (GraphBinary) and
AbstractGraphSONMessageSerializerV4 (GraphSON).
---
.../test/integration/traversal-test.js | 2 +-
.../ser/AbstractGraphSONMessageSerializerV4.java | 2 +-
.../util/ser/binary/RequestMessageSerializer.java | 4 +--
.../util/ser/binary/MessageSerializerTest.java | 37 ++++++++++++++++++++++
4 files changed, 41 insertions(+), 4 deletions(-)
diff --git a/gremlin-js/gremlin-javascript/test/integration/traversal-test.js
b/gremlin-js/gremlin-javascript/test/integration/traversal-test.js
index f543d70a49..1ada7cabff 100644
--- a/gremlin-js/gremlin-javascript/test/integration/traversal-test.js
+++ b/gremlin-js/gremlin-javascript/test/integration/traversal-test.js
@@ -303,7 +303,7 @@ describe('Traversal', function () {
});
it('should allow with_(evaluationTimeout,10)', function() {
const g =
anon.traversal().with_(connection).with_('x').with_('evaluationTimeout', 10);
- return g.V().repeat(__.both()).iterate().then(() => assert.fail("should
have tanked"), (err) => assert.strictEqual(err.statusCode, 400));
+ return g.V().repeat(__.both()).iterate().then(() => assert.fail("should
have tanked"), (err) => assert.strictEqual(err.statusCode, 500));
});
it('should allow SeedStrategy', function () {
const g = anon.traversal().with_(connection).withStrategies(new
SeedStrategy({seed: 999999}));
diff --git
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV4.java
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV4.java
index caa3def020..4fe1be6b72 100644
---
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV4.java
+++
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV4.java
@@ -333,7 +333,7 @@ public abstract class AbstractGraphSONMessageSerializerV4
extends AbstractMessag
builder.addMaterializeProperties(data.get(Tokens.ARGS_MATERIALIZE_PROPERTIES).toString());
}
if (data.containsKey(Tokens.ARGS_BATCH_SIZE)) {
- builder.addChunkSize((int) data.get(Tokens.ARGS_BATCH_SIZE));
+ builder.addChunkSize(((Number)
data.get(Tokens.ARGS_BATCH_SIZE)).intValue());
}
if (data.containsKey(Tokens.BULK_RESULTS)) {
builder.addBulkResults(Boolean.parseBoolean(data.get(Tokens.BULK_RESULTS).toString()));
diff --git
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/binary/RequestMessageSerializer.java
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/binary/RequestMessageSerializer.java
index 9397e5afde..25cc6ab82d 100644
---
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/binary/RequestMessageSerializer.java
+++
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/binary/RequestMessageSerializer.java
@@ -62,13 +62,13 @@ public class RequestMessageSerializer {
builder.addBindings((Map<String, Object>)
fields.get(SerTokens.TOKEN_BINDINGS));
}
if (fields.containsKey(Tokens.TIMEOUT_MS)) {
- builder.addTimeoutMillis((long) fields.get(Tokens.TIMEOUT_MS));
+ builder.addTimeoutMillis(((Number)
fields.get(Tokens.TIMEOUT_MS)).longValue());
}
if (fields.containsKey(Tokens.ARGS_MATERIALIZE_PROPERTIES)) {
builder.addMaterializeProperties(fields.get(Tokens.ARGS_MATERIALIZE_PROPERTIES).toString());
}
if (fields.containsKey(Tokens.ARGS_BATCH_SIZE)) {
- builder.addChunkSize((int) fields.get(Tokens.ARGS_BATCH_SIZE));
+ builder.addChunkSize(((Number)
fields.get(Tokens.ARGS_BATCH_SIZE)).intValue());
}
if (fields.containsKey(Tokens.BULK_RESULTS)) {
builder.addBulkResults(Boolean.parseBoolean(fields.get(Tokens.BULK_RESULTS).toString()));
diff --git
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/MessageSerializerTest.java
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/MessageSerializerTest.java
index 6dfe98c0db..48fa31aa27 100644
---
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/MessageSerializerTest.java
+++
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/MessageSerializerTest.java
@@ -24,18 +24,23 @@ import io.netty.handler.codec.http.HttpResponseStatus;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import
org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader;
+import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter;
import org.apache.tinkerpop.gremlin.util.MessageSerializer;
import org.apache.tinkerpop.gremlin.util.Tokens;
import org.apache.tinkerpop.gremlin.util.message.RequestMessage;
import org.apache.tinkerpop.gremlin.util.message.ResponseMessage;
import org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV4;
import org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV4;
+import org.apache.tinkerpop.gremlin.util.ser.NettyBufferFactory;
import org.apache.tinkerpop.gremlin.util.ser.SerializationException;
+import org.apache.tinkerpop.gremlin.util.ser.SerTokens;
import org.apache.tinkerpop.gremlin.util.ser.Serializers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -142,6 +147,38 @@ public class MessageSerializerTest {
assertResponseEquals(response, deserialized);
}
+ @Test
+ public void shouldDeserializeRequestWithIntegerNumericFields() throws
SerializationException, IOException {
+ // Simulates what happens when a client (e.g., JavaScript) serializes
timeoutMs and batchSize
+ // as GraphBinary INT (4 bytes) instead of LONG/INT. The server must
handle both numeric widths.
+ final GraphBinaryWriter writer = new GraphBinaryWriter();
+ final GraphBinaryReader reader = new GraphBinaryReader();
+ final NettyBufferFactory bufferFactory = new NettyBufferFactory();
+ final RequestMessageSerializer requestSerializer = new
RequestMessageSerializer();
+
+ // Build a fields map with Integer values (as a JS client would
produce for INT32-range values)
+ final Map<String, Object> fields = new HashMap<>();
+ fields.put(SerTokens.TOKEN_LANGUAGE, "gremlin-lang");
+ fields.put(Tokens.TIMEOUT_MS, 5000); // Integer, not Long
+ fields.put(Tokens.ARGS_BATCH_SIZE, 64); // Integer, not Long
(though normally int)
+
+ final String gremlin = "g.V()";
+
+ // Manually write the GraphBinary request buffer with Integer-typed
numeric fields
+ final ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();
+ final org.apache.tinkerpop.gremlin.structure.io.Buffer buffer =
bufferFactory.create(byteBuf);
+ buffer.writeByte(GraphBinaryWriter.VERSION_BYTE);
+ writer.writeValue(fields, buffer, false);
+ writer.writeValue(gremlin, buffer, false);
+
+ final RequestMessage deserialized =
requestSerializer.readValue(byteBuf, reader);
+ assertEquals(5000L, (long) deserialized.getField(Tokens.TIMEOUT_MS));
+ assertEquals(64, (int) deserialized.getField(Tokens.ARGS_BATCH_SIZE));
+ assertEquals(gremlin, deserialized.getGremlin());
+
+ byteBuf.release();
+ }
+
private static void assertResponseEquals(ResponseMessage expected,
ResponseMessage actual) {
// Status
assertEquals(expected.getStatus().getCode(),
actual.getStatus().getCode());