diff --git a/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/JsonHubProtocol.java b/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/JsonHubProtocol.java index 4acc831226..37a09acc92 100644 --- a/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/JsonHubProtocol.java +++ b/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/JsonHubProtocol.java @@ -3,6 +3,7 @@ package com.microsoft.aspnet.signalr; +import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.List; @@ -12,6 +13,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; class JsonHubProtocol implements HubProtocol { private final JsonParser jsonParser = new JsonParser(); @@ -77,15 +79,8 @@ class JsonHubProtocol implements HubProtocol { break; case "arguments": if (target != null) { - reader.beginArray(); List> types = binder.getParameterTypes(target); - if (types != null && types.size() >= 1) { - arguments = new ArrayList<>(); - for (int i = 0; i < types.size(); i++) { - arguments.add(gson.fromJson(reader, types.get(i))); - } - } - reader.endArray(); + arguments = bindArguments(reader, types); } else { argumentsToken = (JsonArray)jsonParser.parse(reader); } @@ -106,12 +101,7 @@ class JsonHubProtocol implements HubProtocol { case INVOCATION: if (argumentsToken != null) { List> types = binder.getParameterTypes(target); - if (types != null && types.size() >= 1) { - arguments = new ArrayList<>(); - for (int i = 0; i < types.size(); i++) { - arguments.add(gson.fromJson(argumentsToken.get(i), types.get(i))); - } - } + arguments = bindArguments(argumentsToken, types); } if (arguments == null) { hubMessages.add(new InvocationMessage(invocationId, target, new Object[0])); @@ -151,4 +141,42 @@ class JsonHubProtocol implements HubProtocol { public String writeMessage(HubMessage hubMessage) { return gson.toJson(hubMessage) + RECORD_SEPARATOR; } + + private ArrayList bindArguments(JsonArray argumentsToken, List> paramTypes) { + if (argumentsToken.size() != paramTypes.size()) { + throw new RuntimeException(String.format("Invocation provides %d argument(s) but target expects %d.", argumentsToken.size(), paramTypes.size())); + } + + ArrayList arguments = null; + if (paramTypes.size() >= 1) { + arguments = new ArrayList<>(); + for (int i = 0; i < paramTypes.size(); i++) { + arguments.add(gson.fromJson(argumentsToken.get(i), paramTypes.get(i))); + } + } + + return arguments; + } + + private ArrayList bindArguments(JsonReader reader, List> paramTypes) throws IOException { + reader.beginArray(); + int paramCount = paramTypes.size(); + int argCount = 0; + ArrayList arguments = new ArrayList<>(); + while (reader.peek() != JsonToken.END_ARRAY) { + if (argCount < paramCount) { + arguments.add(gson.fromJson(reader, paramTypes.get(argCount))); + } else { + reader.skipValue(); + } + argCount++; + } + + if (paramCount != argCount) { + throw new RuntimeException(String.format("Invocation provides %d argument(s) but target expects %d.", argCount, paramCount)); + } + + reader.endArray(); + return arguments; + } } diff --git a/clients/java/signalr/src/test/java/com/microsoft/aspnet/signalr/HubConnectionTest.java b/clients/java/signalr/src/test/java/com/microsoft/aspnet/signalr/HubConnectionTest.java index 0385bb8b6c..abeff622d1 100644 --- a/clients/java/signalr/src/test/java/com/microsoft/aspnet/signalr/HubConnectionTest.java +++ b/clients/java/signalr/src/test/java/com/microsoft/aspnet/signalr/HubConnectionTest.java @@ -841,7 +841,7 @@ class HubConnectionTest { @Test public void callingStartOnStartedHubConnectionNoOps() throws Exception { Transport mockTransport = new MockTransport(); - HubConnection hubConnection = new HubConnection("http://example.com", mockTransport ,new NullLogger() ,true); + HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, new NullLogger() ,true); hubConnection.start(); assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState()); @@ -861,4 +861,20 @@ class HubConnectionTest { Throwable exception = assertThrows(HubException.class, () -> hubConnection.send("inc")); assertEquals("The 'send' method cannot be called if the connection is not active", exception.getMessage()); } + + @Test + public void errorWhenReceivingInvokeWithIncorrectArgumentLength() throws Exception { + MockTransport mockTransport = new MockTransport(); + HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, new NullLogger(), true); + hubConnection.on("Send", (s) -> { + assertTrue(false); + }, String.class); + + CompletableFuture startFuture = hubConnection.start(); + mockTransport.receiveMessage("{}" + RECORD_SEPARATOR); + + startFuture.get(1000, TimeUnit.MILLISECONDS); + RuntimeException exception = assertThrows(RuntimeException.class, () -> mockTransport.receiveMessage("{\"type\":1,\"target\":\"Send\",\"arguments\":[]}" + RECORD_SEPARATOR)); + assertEquals("Invocation provides 0 argument(s) but target expects 1.", exception.getMessage()); + } } \ No newline at end of file diff --git a/clients/java/signalr/src/test/java/com/microsoft/aspnet/signalr/JsonHubProtocolTest.java b/clients/java/signalr/src/test/java/com/microsoft/aspnet/signalr/JsonHubProtocolTest.java index ccd4cd33ec..b637058f15 100644 --- a/clients/java/signalr/src/test/java/com/microsoft/aspnet/signalr/JsonHubProtocolTest.java +++ b/clients/java/signalr/src/test/java/com/microsoft/aspnet/signalr/JsonHubProtocolTest.java @@ -219,6 +219,33 @@ class JsonHubProtocolTest { assertEquals(42 , message.getResult()); } + @Test + public void errorWhileParsingTooManyArgumentsWithOutOfOrderProperties() throws Exception { + String stringifiedMessage = "{\"arguments\":[42, 24],\"type\":1,\"target\":\"test\"}\u001E"; + TestBinder binder = new TestBinder(new InvocationMessage(null, "test", new Object[] { 42 })); + + RuntimeException exception = assertThrows(RuntimeException.class, () -> jsonHubProtocol.parseMessages(stringifiedMessage, binder)); + assertEquals("Invocation provides 2 argument(s) but target expects 1.", exception.getMessage()); + } + + @Test + public void errorWhileParsingTooManyArguments() throws Exception { + String stringifiedMessage = "{\"type\":1,\"target\":\"test\",\"arguments\":[42, 24]}\u001E"; + TestBinder binder = new TestBinder(new InvocationMessage(null, "test", new Object[] { 42 })); + + RuntimeException exception = assertThrows(RuntimeException.class, () -> jsonHubProtocol.parseMessages(stringifiedMessage, binder)); + assertEquals("Invocation provides 2 argument(s) but target expects 1.", exception.getMessage()); + } + + @Test + public void errorWhileParsingTooFewArguments() throws Exception { + String stringifiedMessage = "{\"type\":1,\"target\":\"test\",\"arguments\":[42]}\u001E"; + TestBinder binder = new TestBinder(new InvocationMessage(null, "test", new Object[] { 42, 24 })); + + RuntimeException exception = assertThrows(RuntimeException.class, () -> jsonHubProtocol.parseMessages(stringifiedMessage, binder)); + assertEquals("Invocation provides 1 argument(s) but target expects 2.", exception.getMessage()); + } + private class TestBinder implements InvocationBinder { private Class[] paramTypes = null; private Class returnType = null;