[Java] Log invocation binding failures (#3160)

This commit is contained in:
BrennanConroy 2018-10-30 13:49:43 -07:00 committed by GitHub
parent 73f2f19984
commit c9600ac2d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 124 additions and 26 deletions

View File

@ -167,6 +167,10 @@ public class HubConnection {
for (HubMessage message : messages) {
logger.debug("Received message of type {}.", message.getMessageType());
switch (message.getMessageType()) {
case INVOCATION_BINDING_FAILURE:
InvocationBindingFailureMessage msg = (InvocationBindingFailureMessage)message;
logger.error("Failed to bind arguments received in invocation '{}' of '{}'.", msg.getInvocationId(), msg.getTarget(), msg.getException());
break;
case INVOCATION:
InvocationMessage invocationMessage = (InvocationMessage) message;
List<InvocationHandler> handlers = this.handlers.get(invocationMessage.getTarget());

View File

@ -10,7 +10,8 @@ enum HubMessageType {
STREAM_INVOCATION(4),
CANCEL_INVOCATION(5),
PING(6),
CLOSE(7);
CLOSE(7),
INVOCATION_BINDING_FAILURE(-1);
public int value;
HubMessageType(int id) { this.value = id; }

View File

@ -0,0 +1,33 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
package com.microsoft.signalr;
class InvocationBindingFailureMessage extends HubMessage {
private final String invocationId;
private final String target;
private final Exception exception;
public InvocationBindingFailureMessage(String invocationId, String target, Exception exception) {
this.invocationId = invocationId;
this.target = target;
this.exception = exception;
}
public String getInvocationId() {
return invocationId;
}
public String getTarget() {
return target;
}
public Exception getException() {
return exception;
}
@Override
public HubMessageType getMessageType() {
return HubMessageType.INVOCATION_BINDING_FAILURE;
}
}

View File

@ -52,6 +52,7 @@ class JsonHubProtocol implements HubProtocol {
ArrayList<Object> arguments = null;
JsonArray argumentsToken = null;
Object result = null;
Exception argumentBindingException = null;
JsonElement resultToken = null;
JsonReader reader = new JsonReader(new StringReader(str));
reader.beginObject();
@ -83,8 +84,26 @@ class JsonHubProtocol implements HubProtocol {
break;
case "arguments":
if (target != null) {
List<Class<?>> types = binder.getParameterTypes(target);
arguments = bindArguments(reader, types);
boolean startedArray = false;
try {
List<Class<?>> types = binder.getParameterTypes(target);
startedArray = true;
arguments = bindArguments(reader, types);
} catch (Exception ex) {
argumentBindingException = ex;
// Could be at any point in argument array JSON when an error is thrown
// Read until the end of the argument JSON array
if (!startedArray) {
reader.beginArray();
}
while (reader.hasNext()) {
reader.skipValue();
}
if (reader.peek() == JsonToken.END_ARRAY) {
reader.endArray();
}
}
} else {
argumentsToken = (JsonArray)jsonParser.parse(reader);
}
@ -104,13 +123,21 @@ class JsonHubProtocol implements HubProtocol {
switch (messageType) {
case INVOCATION:
if (argumentsToken != null) {
List<Class<?>> types = binder.getParameterTypes(target);
arguments = bindArguments(argumentsToken, types);
try {
List<Class<?>> types = binder.getParameterTypes(target);
arguments = bindArguments(argumentsToken, types);
} catch (Exception ex) {
argumentBindingException = ex;
}
}
if (arguments == null) {
hubMessages.add(new InvocationMessage(invocationId, target, new Object[0]));
if (argumentBindingException != null) {
hubMessages.add(new InvocationBindingFailureMessage(invocationId, target, argumentBindingException));
} else {
hubMessages.add(new InvocationMessage(invocationId, target, arguments.toArray()));
if (arguments == null) {
hubMessages.add(new InvocationMessage(invocationId, target, new Object[0]));
} else {
hubMessages.add(new InvocationMessage(invocationId, target, arguments.toArray()));
}
}
break;
case COMPLETION:
@ -183,7 +210,11 @@ class JsonHubProtocol implements HubProtocol {
throw new RuntimeException(String.format("Invocation provides %d argument(s) but target expects %d.", argCount, paramCount));
}
// Do this at the very end, because if we throw for any reason above, we catch at the call site
// And manually consume the rest of the array, if we called endArray before throwing the RuntimeException
// Then we can't correctly consume the rest of the json object
reader.endArray();
return arguments;
}
}

View File

@ -81,12 +81,7 @@ class OkHttpWebSocketWrapper extends WebSocketWrapper {
@Override
public void onMessage(WebSocket webSocket, String message) {
try {
onReceive.invoke(message);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
onReceive.invoke(message);
}
@Override

View File

@ -904,7 +904,7 @@ class HubConnectionTest {
}
@Test
public void errorWhenReceivingInvokeWithIncorrectArgumentLength() {
public void doesNotErrorWhenReceivingInvokeWithIncorrectArgumentLength() {
MockTransport mockTransport = new MockTransport();
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
hubConnection.on("Send", (s) -> {
@ -913,8 +913,8 @@ class HubConnectionTest {
hubConnection.start().timeout(1, TimeUnit.SECONDS).blockingAwait();
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());
mockTransport.receiveMessage("{\"type\":1,\"target\":\"Send\",\"arguments\":[]}" + RECORD_SEPARATOR);
hubConnection.stop();
}
@Test

View File

@ -220,30 +220,64 @@ class JsonHubProtocolTest {
}
@Test
public void errorWhileParsingTooManyArgumentsWithOutOfOrderProperties() {
public void invocationBindingFailureWhileParsingTooManyArgumentsWithOutOfOrderProperties() {
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());
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage, binder);
assertEquals(1, messages.length);
assertEquals(InvocationBindingFailureMessage.class, messages[0].getClass());
InvocationBindingFailureMessage message = (InvocationBindingFailureMessage)messages[0];
assertEquals("Invocation provides 2 argument(s) but target expects 1.", message.getException().getMessage());
}
@Test
public void errorWhileParsingTooManyArguments() {
public void invocationBindingFailureWhileParsingTooManyArguments() {
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());
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage, binder);
assertEquals(1, messages.length);
assertEquals(InvocationBindingFailureMessage.class, messages[0].getClass());
InvocationBindingFailureMessage message = (InvocationBindingFailureMessage) messages[0];
assertEquals("Invocation provides 2 argument(s) but target expects 1.", message.getException().getMessage());
}
@Test
public void errorWhileParsingTooFewArguments() {
public void invocationBindingFailureWhileParsingTooFewArguments() {
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());
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage, binder);
assertEquals(1, messages.length);
assertEquals(InvocationBindingFailureMessage.class, messages[0].getClass());
InvocationBindingFailureMessage message = (InvocationBindingFailureMessage) messages[0];
assertEquals("Invocation provides 1 argument(s) but target expects 2.", message.getException().getMessage());
}
@Test
public void invocationBindingFailureWhenParsingIncorrectType() {
String stringifiedMessage = "{\"type\":1,\"target\":\"test\",\"arguments\":[\"true\"]}\u001E";
TestBinder binder = new TestBinder(new InvocationMessage(null, "test", new Object[] { 42 }));
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage, binder);
assertEquals(1, messages.length);
assertEquals(InvocationBindingFailureMessage.class, messages[0].getClass());
InvocationBindingFailureMessage message = (InvocationBindingFailureMessage) messages[0];
assertEquals("java.lang.NumberFormatException: For input string: \"true\"", message.getException().getMessage());
}
@Test
public void invocationBindingFailureStillReadsJsonPayloadAfterFailure() {
String stringifiedMessage = "{\"type\":1,\"target\":\"test\",\"arguments\":[\"true\"],\"invocationId\":\"123\"}\u001E";
TestBinder binder = new TestBinder(new InvocationMessage(null, "test", new Object[] { 42 }));
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage, binder);
assertEquals(1, messages.length);
assertEquals(InvocationBindingFailureMessage.class, messages[0].getClass());
InvocationBindingFailureMessage message = (InvocationBindingFailureMessage) messages[0];
assertEquals("java.lang.NumberFormatException: For input string: \"true\"", message.getException().getMessage());
assertEquals("123", message.getInvocationId());
}
@Test