Merge pull request #2950 from dotnet-maestro-bot/merge/release/2.2-to-master

[automated] Merge branch 'release/2.2' => 'master'
This commit is contained in:
BrennanConroy 2018-09-14 11:37:05 -07:00 committed by GitHub
commit 7af3472ef4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 475 additions and 136 deletions

View File

@ -7,23 +7,27 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.Collections;
class CallbackMap { class CallbackMap {
private ConcurrentHashMap<String, List<ActionBase>> handlers = new ConcurrentHashMap<>(); private ConcurrentHashMap<String, List<InvocationHandler>> handlers = new ConcurrentHashMap<>();
public InvocationHandler put(String target, ActionBase action, ArrayList<Class<?>> classes) {
InvocationHandler handler = new InvocationHandler(action, Collections.unmodifiableList(classes));
public void put(String target, ActionBase action) {
handlers.computeIfPresent(target, (methodName, handlerList) -> { handlers.computeIfPresent(target, (methodName, handlerList) -> {
handlerList.add(action); handlerList.add(handler);
return handlerList; return handlerList;
}); });
handlers.computeIfAbsent(target, (ac) -> new ArrayList<>(Arrays.asList(action))); handlers.computeIfAbsent(target, (ac) -> new ArrayList<>(Arrays.asList(handler)));
return handler;
} }
public Boolean containsKey(String key) { public Boolean containsKey(String key) {
return handlers.containsKey(key); return handlers.containsKey(key);
} }
public List<ActionBase> get(String key) { public List<InvocationHandler> get(String key) {
return handlers.get(key); return handlers.get(key);
} }

View File

@ -9,26 +9,24 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
public class HubConnection { public class HubConnection {
private String url; private String url;
private Transport transport; private Transport transport;
private OnReceiveCallBack callback; private OnReceiveCallBack callback;
private CallbackMap handlers = new CallbackMap(); private CallbackMap handlers = new CallbackMap();
private HubProtocol protocol; private HubProtocol protocol;
private Gson gson = new Gson();
private Boolean handshakeReceived = false; private Boolean handshakeReceived = false;
private static final String RECORD_SEPARATOR = "\u001e"; private static final String RECORD_SEPARATOR = "\u001e";
private HubConnectionState connectionState = HubConnectionState.DISCONNECTED; private HubConnectionState hubConnectionState = HubConnectionState.DISCONNECTED;
private Logger logger; private Logger logger;
private List<Consumer<Exception>> onClosedCallbackList; private List<Consumer<Exception>> onClosedCallbackList;
private boolean skipNegotiate = false; private boolean skipNegotiate = false;
private NegotiateResponse negotiateResponse; private NegotiateResponse negotiateResponse;
private String accessToken; private String accessToken;
private Map<String, String> headers = new HashMap<>(); private Map<String, String> headers = new HashMap<>();
private ConnectionState connectionState = null;
private static ArrayList<Class<?>> emptyArray = new ArrayList<>();
private static int MAX_NEGOTIATE_ATTEMPTS = 100; private static int MAX_NEGOTIATE_ATTEMPTS = 100;
public HubConnection(String url, Transport transport, Logger logger, boolean skipNegotiate) { public HubConnection(String url, Transport transport, Logger logger, boolean skipNegotiate) {
@ -61,24 +59,20 @@ public class HubConnection {
} }
} }
HubMessage[] messages = protocol.parseMessages(payload); HubMessage[] messages = protocol.parseMessages(payload, connectionState);
for (HubMessage message : messages) { for (HubMessage message : messages) {
logger.log(LogLevel.Debug, "Received message of type %s", message.getMessageType()); logger.log(LogLevel.Debug, "Received message of type %s.", message.getMessageType());
switch (message.getMessageType()) { switch (message.getMessageType()) {
case INVOCATION: case INVOCATION:
InvocationMessage invocationMessage = (InvocationMessage) message; InvocationMessage invocationMessage = (InvocationMessage) message;
if (handlers.containsKey(invocationMessage.target)) { List<InvocationHandler> handlers = this.handlers.get(invocationMessage.target);
ArrayList<Object> args = gson.fromJson((JsonArray) invocationMessage.arguments[0], (new ArrayList<>()).getClass()); if (handlers != null) {
List<ActionBase> actions = handlers.get(invocationMessage.target); for (InvocationHandler handler : handlers) {
if (actions != null) { handler.getAction().invoke(invocationMessage.arguments);
logger.log(LogLevel.Debug, "Invoking handlers for target %s", invocationMessage.target);
for (ActionBase action : actions) {
action.invoke(args.toArray());
}
} }
} else { } else {
logger.log(LogLevel.Warning, "Failed to find handler for %s method", invocationMessage.target); logger.log(LogLevel.Warning, "Failed to find handler for %s method.", invocationMessage.target);
} }
break; break;
case CLOSE: case CLOSE:
@ -93,7 +87,7 @@ public class HubConnection {
case STREAM_ITEM: case STREAM_ITEM:
case CANCEL_INVOCATION: case CANCEL_INVOCATION:
case COMPLETION: case COMPLETION:
logger.log(LogLevel.Error, "This client does not support %s messages", message.getMessageType()); logger.log(LogLevel.Error, "This client does not support %s messages.", message.getMessageType());
throw new UnsupportedOperationException(String.format("The message type %s is not supported yet.", message.getMessageType())); throw new UnsupportedOperationException(String.format("The message type %s is not supported yet.", message.getMessageType()));
} }
@ -148,7 +142,7 @@ public class HubConnection {
* @return HubConnection state enum. * @return HubConnection state enum.
*/ */
public HubConnectionState getConnectionState() { public HubConnectionState getConnectionState() {
return connectionState; return hubConnectionState;
} }
/** /**
@ -157,7 +151,7 @@ public class HubConnection {
* @throws Exception An error occurred while connecting. * @throws Exception An error occurred while connecting.
*/ */
public void start() throws Exception { public void start() throws Exception {
if (connectionState != HubConnectionState.DISCONNECTED) { if (hubConnectionState != HubConnectionState.DISCONNECTED) {
return; return;
} }
if (!skipNegotiate) { if (!skipNegotiate) {
@ -198,7 +192,8 @@ public class HubConnection {
transport.start(); transport.start();
String handshake = HandshakeProtocol.createHandshakeRequestMessage(new HandshakeRequestMessage(protocol.getName(), protocol.getVersion())); String handshake = HandshakeProtocol.createHandshakeRequestMessage(new HandshakeRequestMessage(protocol.getName(), protocol.getVersion()));
transport.send(handshake); transport.send(handshake);
connectionState = HubConnectionState.CONNECTED; hubConnectionState = HubConnectionState.CONNECTED;
connectionState = new ConnectionState(this);
logger.log(LogLevel.Information, "HubConnected started."); logger.log(LogLevel.Information, "HubConnected started.");
} }
@ -206,7 +201,7 @@ public class HubConnection {
* Stops a connection to the server. * Stops a connection to the server.
*/ */
private void stop(String errorMessage) { private void stop(String errorMessage) {
if (connectionState == HubConnectionState.DISCONNECTED) { if (hubConnectionState == HubConnectionState.DISCONNECTED) {
return; return;
} }
@ -217,7 +212,8 @@ public class HubConnection {
} }
transport.stop(); transport.stop();
connectionState = HubConnectionState.DISCONNECTED; hubConnectionState = HubConnectionState.DISCONNECTED;
connectionState = null;
logger.log(LogLevel.Information, "HubConnection stopped."); logger.log(LogLevel.Information, "HubConnection stopped.");
if (onClosedCallbackList != null) { if (onClosedCallbackList != null) {
HubException hubException = new HubException(errorMessage); HubException hubException = new HubException(errorMessage);
@ -243,7 +239,7 @@ public class HubConnection {
* @throws Exception If there was an error while sending. * @throws Exception If there was an error while sending.
*/ */
public void send(String method, Object... args) throws Exception { public void send(String method, Object... args) throws Exception {
if (connectionState != HubConnectionState.CONNECTED) { if (hubConnectionState != HubConnectionState.CONNECTED) {
throw new HubException("The 'send' method cannot be called if the connection is not active"); throw new HubException("The 'send' method cannot be called if the connection is not active");
} }
@ -262,9 +258,9 @@ public class HubConnection {
*/ */
public Subscription on(String target, Action callback) { public Subscription on(String target, Action callback) {
ActionBase action = args -> callback.invoke(); ActionBase action = args -> callback.invoke();
handlers.put(target, action); InvocationHandler handler = handlers.put(target, action, emptyArray);
logger.log(LogLevel.Trace, "Registering handler for client method: %s", target); logger.log(LogLevel.Trace, "Registering handler for client method: %s", target);
return new Subscription(handlers, action, target); return new Subscription(handlers, handler, target);
} }
/** /**
@ -278,9 +274,11 @@ public class HubConnection {
*/ */
public <T1> Subscription on(String target, Action1<T1> callback, Class<T1> param1) { public <T1> Subscription on(String target, Action1<T1> callback, Class<T1> param1) {
ActionBase action = params -> callback.invoke(param1.cast(params[0])); ActionBase action = params -> callback.invoke(param1.cast(params[0]));
handlers.put(target, action); ArrayList<Class<?>> classes = new ArrayList<>(1);
classes.add(param1);
InvocationHandler handler = handlers.put(target, action, classes);
logger.log(LogLevel.Trace, "Registering handler for client method: %s", target); logger.log(LogLevel.Trace, "Registering handler for client method: %s", target);
return new Subscription(handlers, action, target); return new Subscription(handlers, handler, target);
} }
/** /**
@ -298,9 +296,12 @@ public class HubConnection {
ActionBase action = params -> { ActionBase action = params -> {
callback.invoke(param1.cast(params[0]), param2.cast(params[1])); callback.invoke(param1.cast(params[0]), param2.cast(params[1]));
}; };
handlers.put(target, action); ArrayList<Class<?>> classes = new ArrayList<>(2);
classes.add(param1);
classes.add(param2);
InvocationHandler handler = handlers.put(target, action, classes);
logger.log(LogLevel.Trace, "Registering handler for client method: %s", target); logger.log(LogLevel.Trace, "Registering handler for client method: %s", target);
return new Subscription(handlers, action, target); return new Subscription(handlers, handler, target);
} }
/** /**
@ -321,9 +322,13 @@ public class HubConnection {
ActionBase action = params -> { ActionBase action = params -> {
callback.invoke(param1.cast(params[0]), param2.cast(params[1]), param3.cast(params[2])); callback.invoke(param1.cast(params[0]), param2.cast(params[1]), param3.cast(params[2]));
}; };
handlers.put(target, action); ArrayList<Class<?>> classes = new ArrayList<>(3);
classes.add(param1);
classes.add(param2);
classes.add(param3);
InvocationHandler handler = handlers.put(target, action, classes);
logger.log(LogLevel.Trace, "Registering handler for client method: %s", target); logger.log(LogLevel.Trace, "Registering handler for client method: %s", target);
return new Subscription(handlers, action, target); return new Subscription(handlers, handler, target);
} }
/** /**
@ -346,9 +351,14 @@ public class HubConnection {
ActionBase action = params -> { ActionBase action = params -> {
callback.invoke(param1.cast(params[0]), param2.cast(params[1]), param3.cast(params[2]), param4.cast(params[3])); callback.invoke(param1.cast(params[0]), param2.cast(params[1]), param3.cast(params[2]), param4.cast(params[3]));
}; };
handlers.put(target, action); ArrayList<Class<?>> classes = new ArrayList<>(4);
classes.add(param1);
classes.add(param2);
classes.add(param3);
classes.add(param4);
InvocationHandler handler = handlers.put(target, action, classes);
logger.log(LogLevel.Trace, "Registering handler for client method: %s", target); logger.log(LogLevel.Trace, "Registering handler for client method: %s", target);
return new Subscription(handlers, action, target); return new Subscription(handlers, handler, target);
} }
/** /**
@ -374,9 +384,15 @@ public class HubConnection {
callback.invoke(param1.cast(params[0]), param2.cast(params[1]), param3.cast(params[2]), param4.cast(params[3]), callback.invoke(param1.cast(params[0]), param2.cast(params[1]), param3.cast(params[2]), param4.cast(params[3]),
param5.cast(params[4])); param5.cast(params[4]));
}; };
handlers.put(target, action); ArrayList<Class<?>> classes = new ArrayList<>(5);
classes.add(param1);
classes.add(param2);
classes.add(param3);
classes.add(param4);
classes.add(param5);
InvocationHandler handler = handlers.put(target, action, classes);
logger.log(LogLevel.Trace, "Registering handler for client method: %s", target); logger.log(LogLevel.Trace, "Registering handler for client method: %s", target);
return new Subscription(handlers, action, target); return new Subscription(handlers, handler, target);
} }
/** /**
@ -404,9 +420,16 @@ public class HubConnection {
callback.invoke(param1.cast(params[0]), param2.cast(params[1]), param3.cast(params[2]), param4.cast(params[3]), callback.invoke(param1.cast(params[0]), param2.cast(params[1]), param3.cast(params[2]), param4.cast(params[3]),
param5.cast(params[4]), param6.cast(params[5])); param5.cast(params[4]), param6.cast(params[5]));
}; };
handlers.put(target, action); ArrayList<Class<?>> classes = new ArrayList<>(6);
classes.add(param1);
classes.add(param2);
classes.add(param3);
classes.add(param4);
classes.add(param5);
classes.add(param6);
InvocationHandler handler = handlers.put(target, action, classes);
logger.log(LogLevel.Trace, "Registering handler for client method: %s", target); logger.log(LogLevel.Trace, "Registering handler for client method: %s", target);
return new Subscription(handlers, action, target); return new Subscription(handlers, handler, target);
} }
/** /**
@ -436,9 +459,17 @@ public class HubConnection {
callback.invoke(param1.cast(params[0]), param2.cast(params[1]), param3.cast(params[2]), param4.cast(params[3]), callback.invoke(param1.cast(params[0]), param2.cast(params[1]), param3.cast(params[2]), param4.cast(params[3]),
param5.cast(params[4]), param6.cast(params[5]), param7.cast(params[6])); param5.cast(params[4]), param6.cast(params[5]), param7.cast(params[6]));
}; };
handlers.put(target, action); ArrayList<Class<?>> classes = new ArrayList<>(7);
classes.add(param1);
classes.add(param2);
classes.add(param3);
classes.add(param4);
classes.add(param5);
classes.add(param6);
classes.add(param7);
InvocationHandler handler = handlers.put(target, action, classes);
logger.log(LogLevel.Trace, "Registering handler for client method: %s", target); logger.log(LogLevel.Trace, "Registering handler for client method: %s", target);
return new Subscription(handlers, action, target); return new Subscription(handlers, handler, target);
} }
/** /**
@ -470,9 +501,18 @@ public class HubConnection {
callback.invoke(param1.cast(params[0]), param2.cast(params[1]), param3.cast(params[2]), param4.cast(params[3]), callback.invoke(param1.cast(params[0]), param2.cast(params[1]), param3.cast(params[2]), param4.cast(params[3]),
param5.cast(params[4]), param6.cast(params[5]), param7.cast(params[6]), param8.cast(params[7])); param5.cast(params[4]), param6.cast(params[5]), param7.cast(params[6]), param8.cast(params[7]));
}; };
handlers.put(target, action); ArrayList<Class<?>> classes = new ArrayList<>(8);
classes.add(param1);
classes.add(param2);
classes.add(param3);
classes.add(param4);
classes.add(param5);
classes.add(param6);
classes.add(param7);
classes.add(param8);
InvocationHandler handler = handlers.put(target, action, classes);
logger.log(LogLevel.Trace, "Registering handler for client method: %s", target); logger.log(LogLevel.Trace, "Registering handler for client method: %s", target);
return new Subscription(handlers, action, target); return new Subscription(handlers, handler, target);
} }
/** /**
@ -492,4 +532,32 @@ public class HubConnection {
onClosedCallbackList.add(callback); onClosedCallbackList.add(callback);
} }
private class ConnectionState implements InvocationBinder {
HubConnection connection;
public ConnectionState(HubConnection connection) {
this.connection = connection;
}
@Override
public Class<?> getReturnType(String invocationId) {
return null;
}
@Override
public List<Class<?>> getParameterTypes(String methodName) throws Exception {
List<InvocationHandler> handlers = connection.handlers.get(methodName);
if (handlers == null) {
logger.log(LogLevel.Warning, "Failed to find handler for '%s' method.", methodName);
return emptyArray;
}
if (handlers.size() == 0) {
throw new Exception(String.format("There are no callbacks registered for the method '%s'.", methodName));
}
return handlers.get(0).getClasses();
}
}
} }

View File

@ -3,6 +3,8 @@
package com.microsoft.aspnet.signalr; package com.microsoft.aspnet.signalr;
import java.io.IOException;
/** /**
* A protocol abstraction for communicating with SignalR hubs. * A protocol abstraction for communicating with SignalR hubs.
*/ */
@ -16,7 +18,7 @@ interface HubProtocol {
* @param message A string representation of one or more {@link HubMessage}s. * @param message A string representation of one or more {@link HubMessage}s.
* @return A list of {@link HubMessage}s. * @return A list of {@link HubMessage}s.
*/ */
HubMessage[] parseMessages(String message); HubMessage[] parseMessages(String message, InvocationBinder binder) throws Exception;
/** /**
* Writes the specified {@link HubMessage} to a String. * Writes the specified {@link HubMessage} to a String.

View File

@ -0,0 +1,11 @@
// 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.aspnet.signalr;
import java.util.List;
interface InvocationBinder {
Class<?> getReturnType(String invocationId);
List<Class<?>> getParameterTypes(String methodName) throws Exception;
}

View File

@ -0,0 +1,24 @@
// 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.aspnet.signalr;
import java.util.List;
class InvocationHandler {
private List<Class<?>> classes;
private ActionBase action;
InvocationHandler(ActionBase action, List<Class<?>> classes) {
this.action = action;
this.classes = classes;
}
public List<Class<?>> getClasses() {
return classes;
}
public ActionBase getAction() {
return action;
}
}

View File

@ -11,7 +11,7 @@ class InvocationMessage extends HubMessage {
public InvocationMessage(String target, Object[] args) { public InvocationMessage(String target, Object[] args) {
this.target = target; this.target = target;
arguments = args; this.arguments = args;
} }
public String getInvocationId() { public String getInvocationId() {

View File

@ -3,13 +3,16 @@
package com.microsoft.aspnet.signalr; package com.microsoft.aspnet.signalr;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonElement; import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
class JsonHubProtocol implements HubProtocol { class JsonHubProtocol implements HubProtocol {
private final JsonParser jsonParser = new JsonParser(); private final JsonParser jsonParser = new JsonParser();
@ -32,41 +35,105 @@ class JsonHubProtocol implements HubProtocol {
} }
@Override @Override
public HubMessage[] parseMessages(String payload) { public HubMessage[] parseMessages(String payload, InvocationBinder binder) throws Exception {
String[] messages = payload.split(RECORD_SEPARATOR); String[] messages = payload.split(RECORD_SEPARATOR);
List<HubMessage> hubMessages = new ArrayList<>(); List<HubMessage> hubMessages = new ArrayList<>();
for (String splitMessage : messages) { for (String str : messages) {
HubMessageType messageType = null;
String invocationId = null;
String target = null;
String error = null;
ArrayList<Object> arguments = null;
JsonArray argumentsToken = null;
JsonReader reader = new JsonReader(new StringReader(str));
reader.beginObject();
do {
String name = reader.nextName();
switch (name) {
case "type":
messageType = HubMessageType.values()[reader.nextInt() - 1];
break;
case "invocationId":
invocationId = reader.nextString();
break;
case "target":
target = reader.nextString();
break;
case "error":
error = reader.nextString();
break;
case "result":
reader.skipValue();
break;
case "item":
reader.skipValue();
break;
case "arguments":
if (target != null) {
reader.beginArray();
List<Class<?>> 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();
} else {
argumentsToken = (JsonArray)jsonParser.parse(reader);
}
break;
case "headers":
throw new HubException("Headers not implemented yet.");
default:
// Skip unknown property, allows new clients to still work with old protocols
reader.skipValue();
break;
}
} while (reader.hasNext());
reader.endObject();
reader.close();
JsonObject jsonMessage = jsonParser.parse(splitMessage).getAsJsonObject();
HubMessageType messageType = HubMessageType.values()[jsonMessage.get("type").getAsInt() -1];
switch (messageType) { switch (messageType) {
case INVOCATION: case INVOCATION:
//Invocation Message if (argumentsToken != null) {
String target = jsonMessage.get("target").getAsString(); List<Class<?>> types = binder.getParameterTypes(target);
JsonElement args = jsonMessage.get("arguments"); if (types != null && types.size() >= 1) {
hubMessages.add(new InvocationMessage(target, new Object[] {args})); arguments = new ArrayList<>();
for (int i = 0; i < types.size(); i++) {
arguments.add(gson.fromJson(argumentsToken.get(i), types.get(i)));
}
}
}
if (arguments == null) {
hubMessages.add(new InvocationMessage(target, new Object[0]));
} else {
hubMessages.add(new InvocationMessage(target, arguments.toArray()));
}
break; break;
case STREAM_INVOCATION:
case STREAM_ITEM: case STREAM_ITEM:
case COMPLETION: case COMPLETION:
case STREAM_INVOCATION:
case CANCEL_INVOCATION: case CANCEL_INVOCATION:
throw new UnsupportedOperationException(String.format("The message type %s is not supported yet.", messageType)); throw new UnsupportedOperationException(String.format("The message type %s is not supported yet.", messageType));
case PING: case PING:
//Ping
hubMessages.add(new PingMessage()); hubMessages.add(new PingMessage());
break; break;
case CLOSE: case CLOSE:
CloseMessage closeMessage; if (error != null) {
if (jsonMessage.has("error")) { hubMessages.add(new CloseMessage(error));
String error = jsonMessage.get("error").getAsString();
closeMessage = new CloseMessage(error);
} else { } else {
closeMessage = new CloseMessage(); hubMessages.add(new CloseMessage());
} }
hubMessages.add(closeMessage); break;
default:
break; break;
} }
} }
return hubMessages.toArray(new HubMessage[hubMessages.size()]); return hubMessages.toArray(new HubMessage[hubMessages.size()]);
} }

View File

@ -0,0 +1,19 @@
// 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.aspnet.signalr;
class StreamInvocationMessage extends InvocationMessage {
int type = HubMessageType.STREAM_INVOCATION.value;
public StreamInvocationMessage(String invocationId, String target, Object[] arguments) {
super(target, arguments);
this.invocationId = invocationId;
}
@Override
public HubMessageType getMessageType() {
return HubMessageType.STREAM_INVOCATION;
}
}

View File

@ -7,19 +7,19 @@ import java.util.List;
public class Subscription { public class Subscription {
private CallbackMap handlers; private CallbackMap handlers;
private ActionBase action; private InvocationHandler handler;
private String target; private String target;
public Subscription(CallbackMap handlers, ActionBase action, String target) { public Subscription(CallbackMap handlers, InvocationHandler handler, String target) {
this.handlers = handlers; this.handlers = handlers;
this.action = action; this.handler = handler;
this.target = target; this.target = target;
} }
public void unsubscribe() { public void unsubscribe() {
List<ActionBase> actions = this.handlers.get(target); List<InvocationHandler> handler = this.handlers.get(target);
if (actions != null) { if (handler != null) {
actions.remove(action); handler.remove(this.handler);
} }
} }
} }

View File

@ -31,7 +31,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void HubConnectionClosesAfterCloseMessage() throws Exception { public void hubConnectionClosesAfterCloseMessage() throws Exception {
MockTransport mockTransport = new MockTransport(); MockTransport mockTransport = new MockTransport();
HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true); HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true);
@ -46,7 +46,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void HubConnectionReceiveHandshakeResponseWithError() throws Exception { public void hubConnectionReceiveHandshakeResponseWithError() throws Exception {
exceptionRule.expect(HubException.class); exceptionRule.expect(HubException.class);
exceptionRule.expectMessage("Requested protocol 'messagepack' is not available."); exceptionRule.expectMessage("Requested protocol 'messagepack' is not available.");
@ -58,7 +58,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void RegisteringMultipleHandlersAndBothGetTriggered() throws Exception { public void registeringMultipleHandlersAndBothGetTriggered() throws Exception {
AtomicReference<Double> value = new AtomicReference<>(0.0); AtomicReference<Double> value = new AtomicReference<>(0.0);
MockTransport mockTransport = new MockTransport(); MockTransport mockTransport = new MockTransport();
HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true); HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true);
@ -84,7 +84,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void RemoveHandlerByName() throws Exception { public void removeHandlerByName() throws Exception {
AtomicReference<Double> value = new AtomicReference<>(0.0); AtomicReference<Double> value = new AtomicReference<>(0.0);
MockTransport mockTransport = new MockTransport(); MockTransport mockTransport = new MockTransport();
HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true); HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true);
@ -111,7 +111,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void AddAndRemoveHandlerImmediately() throws Exception { public void addAndRemoveHandlerImmediately() throws Exception {
AtomicReference<Double> value = new AtomicReference<>(0.0); AtomicReference<Double> value = new AtomicReference<>(0.0);
MockTransport mockTransport = new MockTransport(); MockTransport mockTransport = new MockTransport();
HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true); HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true);
@ -132,11 +132,11 @@ public class HubConnectionTest {
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR); mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
// Confirming that the handler was removed. // Confirming that the handler was removed.
assertEquals(0, value.get(), 0); assertEquals(0.0, value.get(), 0);
} }
@Test @Test
public void RemovingMultipleHandlersWithOneCallToRemove() throws Exception { public void removingMultipleHandlersWithOneCallToRemove() throws Exception {
AtomicReference<Double> value = new AtomicReference<>(0.0); AtomicReference<Double> value = new AtomicReference<>(0.0);
MockTransport mockTransport = new MockTransport(); MockTransport mockTransport = new MockTransport();
HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true); HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true);
@ -168,7 +168,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void RemoveHandlerWithUnsubscribe() throws Exception { public void removeHandlerWithUnsubscribe() throws Exception {
AtomicReference<Double> value = new AtomicReference<>(0.0); AtomicReference<Double> value = new AtomicReference<>(0.0);
MockTransport mockTransport = new MockTransport(); MockTransport mockTransport = new MockTransport();
HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true); HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true);
@ -191,12 +191,17 @@ public class HubConnectionTest {
assertEquals(1, value.get(), 0); assertEquals(1, value.get(), 0);
subscription.unsubscribe(); subscription.unsubscribe();
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR); try {
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
} catch (Exception ex) {
assertEquals("There are no callbacks registered for the method 'inc'.", ex.getMessage());
}
assertEquals(1, value.get(), 0); assertEquals(1, value.get(), 0);
} }
@Test @Test
public void UnsubscribeTwice() throws Exception { public void unsubscribeTwice() throws Exception {
AtomicReference<Double> value = new AtomicReference<>(0.0); AtomicReference<Double> value = new AtomicReference<>(0.0);
MockTransport mockTransport = new MockTransport(); MockTransport mockTransport = new MockTransport();
HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true); HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true);
@ -220,12 +225,17 @@ public class HubConnectionTest {
subscription.unsubscribe(); subscription.unsubscribe();
subscription.unsubscribe(); subscription.unsubscribe();
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR); try {
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
} catch (Exception ex) {
assertEquals("There are no callbacks registered for the method 'inc'.", ex.getMessage());
}
assertEquals(1, value.get(), 0); assertEquals(1, value.get(), 0);
} }
@Test @Test
public void RemoveSingleHandlerWithUnsubscribe() throws Exception { public void removeSingleHandlerWithUnsubscribe() throws Exception {
AtomicReference<Double> value = new AtomicReference<>(0.0); AtomicReference<Double> value = new AtomicReference<>(0.0);
MockTransport mockTransport = new MockTransport(); MockTransport mockTransport = new MockTransport();
HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true); HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true);
@ -255,7 +265,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void AddAndRemoveHandlerImmediatelyWithSubscribe() throws Exception { public void addAndRemoveHandlerImmediatelyWithSubscribe() throws Exception {
AtomicReference<Double> value = new AtomicReference<>(0.0); AtomicReference<Double> value = new AtomicReference<>(0.0);
MockTransport mockTransport = new MockTransport(); MockTransport mockTransport = new MockTransport();
HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true); HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true);
@ -268,13 +278,19 @@ public class HubConnectionTest {
hubConnection.start(); hubConnection.start();
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR); mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
try {
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
} catch (Exception ex) {
assertEquals("There are no callbacks registered for the method 'inc'.", ex.getMessage());
}
// Confirming that the handler was removed. // Confirming that the handler was removed.
assertEquals(0, value.get(), 0); assertEquals(0, value.get(), 0);
} }
@Test @Test
public void RegisteringMultipleHandlersThatTakeParamsAndBothGetTriggered() throws Exception { public void registeringMultipleHandlersThatTakeParamsAndBothGetTriggered() throws Exception {
AtomicReference<Double> value = new AtomicReference<>(0.0); AtomicReference<Double> value = new AtomicReference<>(0.0);
MockTransport mockTransport = new MockTransport(); MockTransport mockTransport = new MockTransport();
HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true); HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true);
@ -296,7 +312,7 @@ public class HubConnectionTest {
// We're using AtomicReference<Double> in the send tests instead of int here because Gson has trouble deserializing to Integer // We're using AtomicReference<Double> in the send tests instead of int here because Gson has trouble deserializing to Integer
@Test @Test
public void SendWithNoParamsTriggersOnHandler() throws Exception { public void sendWithNoParamsTriggersOnHandler() throws Exception {
AtomicReference<Double> value = new AtomicReference<Double>(0.0); AtomicReference<Double> value = new AtomicReference<Double>(0.0);
MockTransport mockTransport = new MockTransport(); MockTransport mockTransport = new MockTransport();
HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true); HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true);
@ -315,7 +331,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void SendWithParamTriggersOnHandler() throws Exception { public void sendWithParamTriggersOnHandler() throws Exception {
AtomicReference<String> value = new AtomicReference<>(); AtomicReference<String> value = new AtomicReference<>();
MockTransport mockTransport = new MockTransport(); MockTransport mockTransport = new MockTransport();
HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true); HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true);
@ -335,7 +351,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void SendWithTwoParamsTriggersOnHandler() throws Exception { public void sendWithTwoParamsTriggersOnHandler() throws Exception {
AtomicReference<String> value1 = new AtomicReference<>(); AtomicReference<String> value1 = new AtomicReference<>();
AtomicReference<Double> value2 = new AtomicReference<>(); AtomicReference<Double> value2 = new AtomicReference<>();
@ -361,7 +377,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void SendWithThreeParamsTriggersOnHandler() throws Exception { public void sendWithThreeParamsTriggersOnHandler() throws Exception {
AtomicReference<String> value1 = new AtomicReference<>(); AtomicReference<String> value1 = new AtomicReference<>();
AtomicReference<String> value2 = new AtomicReference<>(); AtomicReference<String> value2 = new AtomicReference<>();
AtomicReference<String> value3 = new AtomicReference<>(); AtomicReference<String> value3 = new AtomicReference<>();
@ -391,7 +407,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void SendWithFourParamsTriggersOnHandler() throws Exception { public void sendWithFourParamsTriggersOnHandler() throws Exception {
AtomicReference<String> value1 = new AtomicReference<>(); AtomicReference<String> value1 = new AtomicReference<>();
AtomicReference<String> value2 = new AtomicReference<>(); AtomicReference<String> value2 = new AtomicReference<>();
AtomicReference<String> value3 = new AtomicReference<>(); AtomicReference<String> value3 = new AtomicReference<>();
@ -424,7 +440,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void SendWithFiveParamsTriggersOnHandler() throws Exception { public void sendWithFiveParamsTriggersOnHandler() throws Exception {
AtomicReference<String> value1 = new AtomicReference<>(); AtomicReference<String> value1 = new AtomicReference<>();
AtomicReference<String> value2 = new AtomicReference<>(); AtomicReference<String> value2 = new AtomicReference<>();
AtomicReference<String> value3 = new AtomicReference<>(); AtomicReference<String> value3 = new AtomicReference<>();
@ -461,7 +477,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void SendWithSixParamsTriggersOnHandler() throws Exception { public void sendWithSixParamsTriggersOnHandler() throws Exception {
AtomicReference<String> value1 = new AtomicReference<>(); AtomicReference<String> value1 = new AtomicReference<>();
AtomicReference<String> value2 = new AtomicReference<>(); AtomicReference<String> value2 = new AtomicReference<>();
AtomicReference<String> value3 = new AtomicReference<>(); AtomicReference<String> value3 = new AtomicReference<>();
@ -502,7 +518,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void SendWithSevenParamsTriggersOnHandler() throws Exception { public void sendWithSevenParamsTriggersOnHandler() throws Exception {
AtomicReference<String> value1 = new AtomicReference<>(); AtomicReference<String> value1 = new AtomicReference<>();
AtomicReference<String> value2 = new AtomicReference<>(); AtomicReference<String> value2 = new AtomicReference<>();
AtomicReference<String> value3 = new AtomicReference<>(); AtomicReference<String> value3 = new AtomicReference<>();
@ -547,7 +563,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void SendWithEightParamsTriggersOnHandler() throws Exception { public void sendWithEightParamsTriggersOnHandler() throws Exception {
AtomicReference<String> value1 = new AtomicReference<>(); AtomicReference<String> value1 = new AtomicReference<>();
AtomicReference<String> value2 = new AtomicReference<>(); AtomicReference<String> value2 = new AtomicReference<>();
AtomicReference<String> value3 = new AtomicReference<>(); AtomicReference<String> value3 = new AtomicReference<>();
@ -594,8 +610,40 @@ public class HubConnectionTest {
assertEquals("F", value8.get()); assertEquals("F", value8.get());
} }
private class Custom {
public int number;
public String str;
public boolean[] bools;
}
@Test @Test
public void ReceiveHandshakeResponseAndMessage() throws Exception { public void sendWithCustomObjectTriggersOnHandler() throws Exception {
AtomicReference<Custom> value1 = new AtomicReference<>();
MockTransport mockTransport = new MockTransport();
HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true);
hubConnection.on("inc", (param1) -> {
assertNull(value1.get());
value1.set(param1);
}, Custom.class);
hubConnection.start();
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[{\"number\":1,\"str\":\"A\",\"bools\":[true,false]}]}" + RECORD_SEPARATOR);
// Confirming that our handler was called and the correct message was passed in.
Custom custom = value1.get();
assertEquals(1, custom.number);
assertEquals("A", custom.str);
assertEquals(2, custom.bools.length);
assertEquals(true, custom.bools[0]);
assertEquals(false, custom.bools[1]);
}
@Test
public void receiveHandshakeResponseAndMessage() throws Exception {
AtomicReference<Double> value = new AtomicReference<Double>(0.0); AtomicReference<Double> value = new AtomicReference<Double>(0.0);
MockTransport mockTransport = new MockTransport(); MockTransport mockTransport = new MockTransport();
HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true); HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true);
@ -660,7 +708,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void HubConnectionClosesAndRunsOnClosedCallbackAfterCloseMessageWithError() throws Exception { public void hubConnectionClosesAndRunsOnClosedCallbackAfterCloseMessageWithError() throws Exception {
MockTransport mockTransport = new MockTransport(); MockTransport mockTransport = new MockTransport();
HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true); HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true);
hubConnection.onClosed((ex) -> { hubConnection.onClosed((ex) -> {
@ -677,7 +725,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void CallingStartOnStartedHubConnectionNoOps() throws Exception { public void callingStartOnStartedHubConnectionNoOps() throws Exception {
Transport mockTransport = new MockTransport(); Transport mockTransport = new MockTransport();
HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true); HubConnection hubConnection = new HubConnection("http://example.com", mockTransport, true);
hubConnection.start(); hubConnection.start();
@ -691,7 +739,7 @@ public class HubConnectionTest {
} }
@Test @Test
public void CannotSendBeforeStart() throws Exception { public void cannotSendBeforeStart() throws Exception {
exceptionRule.expect(HubException.class); exceptionRule.expect(HubException.class);
exceptionRule.expectMessage("The 'send' method cannot be called if the connection is not active"); exceptionRule.expectMessage("The 'send' method cannot be called if the connection is not active");

View File

@ -5,6 +5,11 @@ package com.microsoft.aspnet.signalr;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.PriorityBlockingQueue;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
@ -30,7 +35,7 @@ public class JsonHubProtocolTest {
} }
@Test @Test
public void VerifyWriteMessage() { public void verifyWriteMessage() {
InvocationMessage invocationMessage = new InvocationMessage("test", new Object[] {"42"}); InvocationMessage invocationMessage = new InvocationMessage("test", new Object[] {"42"});
String result = jsonHubProtocol.writeMessage(invocationMessage); String result = jsonHubProtocol.writeMessage(invocationMessage);
String expectedResult = "{\"type\":1,\"target\":\"test\",\"arguments\":[\"42\"]}\u001E"; String expectedResult = "{\"type\":1,\"target\":\"test\",\"arguments\":[\"42\"]}\u001E";
@ -38,9 +43,11 @@ public class JsonHubProtocolTest {
} }
@Test @Test
public void ParsePingMessage() { public void parsePingMessage() throws Exception {
String stringifiedMessage = "{\"type\":6}\u001E"; String stringifiedMessage = "{\"type\":6}\u001E";
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage); TestBinder binder = new TestBinder(new PingMessage());
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage, binder);
//We know it's only one message //We know it's only one message
assertEquals(1, messages.length); assertEquals(1, messages.length);
@ -48,9 +55,11 @@ public class JsonHubProtocolTest {
} }
@Test @Test
public void ParseCloseMessage() { public void parseCloseMessage() throws Exception {
String stringifiedMessage = "{\"type\":7}\u001E"; String stringifiedMessage = "{\"type\":7}\u001E";
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage); TestBinder binder = new TestBinder(new CloseMessage());
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage, binder);
//We know it's only one message //We know it's only one message
assertEquals(1, messages.length); assertEquals(1, messages.length);
@ -64,9 +73,11 @@ public class JsonHubProtocolTest {
} }
@Test @Test
public void ParseCloseMessageWithError() { public void parseCloseMessageWithError() throws Exception {
String stringifiedMessage = "{\"type\":7,\"error\": \"There was an error\"}\u001E"; String stringifiedMessage = "{\"type\":7,\"error\": \"There was an error\"}\u001E";
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage); TestBinder binder = new TestBinder(new CloseMessage("There was an error"));
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage, binder);
//We know it's only one message //We know it's only one message
assertEquals(1, messages.length); assertEquals(1, messages.length);
@ -80,9 +91,11 @@ public class JsonHubProtocolTest {
} }
@Test @Test
public void ParseSingleMessage() { public void parseSingleMessage() throws Exception {
String stringifiedMessage = "{\"type\":1,\"target\":\"test\",\"arguments\":[42]}\u001E"; String stringifiedMessage = "{\"type\":1,\"target\":\"test\",\"arguments\":[42]}\u001E";
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage); TestBinder binder = new TestBinder(new InvocationMessage("test", new Object[] { 42 }));
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage, binder);
//We know it's only one message //We know it's only one message
assertEquals(1, messages.length); assertEquals(1, messages.length);
@ -95,50 +108,59 @@ public class JsonHubProtocolTest {
assertEquals("test", invocationMessage.getTarget()); assertEquals("test", invocationMessage.getTarget());
assertEquals(null, invocationMessage.getInvocationId()); assertEquals(null, invocationMessage.getInvocationId());
JsonArray messageResult = (JsonArray) invocationMessage.getArguments()[0]; int messageResult = (int)invocationMessage.getArguments()[0];
assertEquals(42, messageResult.getAsInt()); assertEquals(42, messageResult);
} }
@Rule @Rule
public ExpectedException exceptionRule = ExpectedException.none(); public ExpectedException exceptionRule = ExpectedException.none();
@Test @Test
public void ParseSingleUnsupportedStreamItemMessage() { public void parseSingleUnsupportedStreamItemMessage() throws Exception {
exceptionRule.expect(UnsupportedOperationException.class); exceptionRule.expect(UnsupportedOperationException.class);
exceptionRule.expectMessage("The message type STREAM_ITEM is not supported yet."); exceptionRule.expectMessage("The message type STREAM_ITEM is not supported yet.");
String stringifiedMessage = "{\"type\":2,\"Id\":1,\"Item\":42}\u001E"; String stringifiedMessage = "{\"type\":2,\"Id\":1,\"Item\":42}\u001E";
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage); TestBinder binder = new TestBinder(null);
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage, binder);
} }
@Test @Test
public void ParseSingleUnsupportedStreamInvocationMessage() { public void parseSingleUnsupportedStreamInvocationMessage() throws Exception {
exceptionRule.expect(UnsupportedOperationException.class); exceptionRule.expect(UnsupportedOperationException.class);
exceptionRule.expectMessage("The message type STREAM_INVOCATION is not supported yet."); exceptionRule.expectMessage("The message type STREAM_INVOCATION is not supported yet.");
String stringifiedMessage = "{\"type\":4,\"Id\":1,\"target\":\"test\",\"arguments\":[42]}\u001E"; String stringifiedMessage = "{\"type\":4,\"Id\":1,\"target\":\"test\",\"arguments\":[42]}\u001E";
TestBinder binder = new TestBinder(new StreamInvocationMessage("1", "test", new Object[] { 42 }));
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage); HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage, binder);
} }
@Test @Test
public void ParseSingleUnsupportedCancelInvocationMessage() { public void parseSingleUnsupportedCancelInvocationMessage() throws Exception {
exceptionRule.expect(UnsupportedOperationException.class); exceptionRule.expect(UnsupportedOperationException.class);
exceptionRule.expectMessage("The message type CANCEL_INVOCATION is not supported yet."); exceptionRule.expectMessage("The message type CANCEL_INVOCATION is not supported yet.");
String stringifiedMessage = "{\"type\":5,\"invocationId\":123}\u001E"; String stringifiedMessage = "{\"type\":5,\"invocationId\":123}\u001E";
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage); TestBinder binder = new TestBinder(null);
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage, binder);
} }
@Test @Test
public void ParseSingleUnsupportedCompletionMessage() { public void parseSingleUnsupportedCompletionMessage() throws Exception {
exceptionRule.expect(UnsupportedOperationException.class); exceptionRule.expect(UnsupportedOperationException.class);
exceptionRule.expectMessage("The message type COMPLETION is not supported yet."); exceptionRule.expectMessage("The message type COMPLETION is not supported yet.");
String stringifiedMessage = "{\"type\":3,\"invocationId\":123}\u001E"; String stringifiedMessage = "{\"type\":3,\"invocationId\":123}\u001E";
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage); TestBinder binder = new TestBinder(null);
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage, binder);
} }
@Test @Test
public void ParseTwoMessages() { public void parseTwoMessages() throws Exception {
String twoMessages = "{\"type\":1,\"target\":\"one\",\"arguments\":[42]}\u001E{\"type\":1,\"target\":\"two\",\"arguments\":[43]}\u001E"; String twoMessages = "{\"type\":1,\"target\":\"one\",\"arguments\":[42]}\u001E{\"type\":1,\"target\":\"two\",\"arguments\":[43]}\u001E";
HubMessage[] messages = jsonHubProtocol.parseMessages(twoMessages); TestBinder binder = new TestBinder(new InvocationMessage("one", new Object[] { 42 }));
HubMessage[] messages = jsonHubProtocol.parseMessages(twoMessages, binder);
assertEquals(2, messages.length); assertEquals(2, messages.length);
// Check the first message // Check the first message
@ -149,8 +171,8 @@ public class JsonHubProtocolTest {
assertEquals("one", invocationMessage.getTarget()); assertEquals("one", invocationMessage.getTarget());
assertEquals(null, invocationMessage.getInvocationId()); assertEquals(null, invocationMessage.getInvocationId());
JsonArray messageResult = (JsonArray) invocationMessage.getArguments()[0]; int messageResult = (int)invocationMessage.getArguments()[0];
assertEquals(42, messageResult.getAsInt()); assertEquals(42, messageResult);
// Check the second message // Check the second message
assertEquals(HubMessageType.INVOCATION, messages[1].getMessageType()); assertEquals(HubMessageType.INVOCATION, messages[1].getMessageType());
@ -160,14 +182,16 @@ public class JsonHubProtocolTest {
assertEquals("two", invocationMessage2.getTarget()); assertEquals("two", invocationMessage2.getTarget());
assertEquals(null, invocationMessage2.getInvocationId()); assertEquals(null, invocationMessage2.getInvocationId());
JsonArray secondMessageResult = (JsonArray) invocationMessage2.getArguments()[0]; int secondMessageResult = (int)invocationMessage2.getArguments()[0];
assertEquals(43, secondMessageResult.getAsInt()); assertEquals(43, secondMessageResult);
} }
@Test @Test
public void ParseSingleMessageMutipleArgs() { public void parseSingleMessageMutipleArgs() throws Exception {
String stringifiedMessage = "{\"type\":1,\"target\":\"test\",\"arguments\":[42, 24]}\u001E"; String stringifiedMessage = "{\"type\":1,\"target\":\"test\",\"arguments\":[42, 24]}\u001E";
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage); TestBinder binder = new TestBinder(new InvocationMessage("test", new Object[] { 42, 24 }));
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage, binder);
//We know it's only one message //We know it's only one message
assertEquals(HubMessageType.INVOCATION, messages[0].getMessageType()); assertEquals(HubMessageType.INVOCATION, messages[0].getMessageType());
@ -175,8 +199,72 @@ public class JsonHubProtocolTest {
InvocationMessage message = (InvocationMessage)messages[0]; InvocationMessage message = (InvocationMessage)messages[0];
assertEquals("test", message.getTarget()); assertEquals("test", message.getTarget());
assertEquals(null, message.getInvocationId()); assertEquals(null, message.getInvocationId());
JsonArray messageResult = ((JsonArray) message.getArguments()[0]); int messageResult = (int) message.getArguments()[0];
assertEquals(42, messageResult.get(0).getAsInt()); int messageResult2 = (int) message.getArguments()[1];
assertEquals(24, messageResult.get(1).getAsInt()); assertEquals(42, messageResult);
assertEquals(24, messageResult2);
}
@Test
public void parseMessageWithOutOfOrderProperties() throws Exception {
String stringifiedMessage = "{\"arguments\":[42, 24],\"type\":1,\"target\":\"test\"}\u001E";
TestBinder binder = new TestBinder(new InvocationMessage("test", new Object[] { 42, 24 }));
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage, binder);
// We know it's only one message
assertEquals(HubMessageType.INVOCATION, messages[0].getMessageType());
InvocationMessage message = (InvocationMessage) messages[0];
assertEquals("test", message.getTarget());
assertEquals(null, message.getInvocationId());
int messageResult = (int) message.getArguments()[0];
int messageResult2 = (int) message.getArguments()[1];
assertEquals(42, messageResult);
assertEquals(24, messageResult2);
}
private class TestBinder implements InvocationBinder {
private Class<?>[] paramTypes = null;
public TestBinder(HubMessage expectedMessage) {
if (expectedMessage == null) {
return;
}
switch (expectedMessage.getMessageType()) {
case STREAM_INVOCATION:
ArrayList<Class<?>> streamTypes = new ArrayList<>();
for (Object obj : ((StreamInvocationMessage) expectedMessage).getArguments()) {
streamTypes.add(obj.getClass());
}
paramTypes = streamTypes.toArray(new Class<?>[streamTypes.size()]);
break;
case INVOCATION:
ArrayList<Class<?>> types = new ArrayList<>();
for (Object obj : ((InvocationMessage) expectedMessage).getArguments()) {
types.add(obj.getClass());
}
paramTypes = types.toArray(new Class<?>[types.size()]);
break;
case STREAM_ITEM:
break;
default:
break;
}
}
@Override
public Class<?> getReturnType(String invocationId) {
return null;
}
@Override
public List<Class<?>> getParameterTypes(String methodName) {
if (paramTypes == null) {
return new ArrayList<>();
}
return new ArrayList<Class<?>>(Arrays.asList(paramTypes));
}
} }
} }

View File

@ -228,17 +228,17 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
completed = true; completed = true;
break; break;
default: default:
throw new InvalidDataException($"Unexpected token '{reader.TokenType}' when reading handshake request JSON."); throw new InvalidDataException($"Unexpected token '{reader.TokenType}' when reading handshake request JSON. Message content: {GetPayloadAsString()}");
} }
} }
if (protocol == null) if (protocol == null)
{ {
throw new InvalidDataException($"Missing required property '{ProtocolPropertyName}'."); throw new InvalidDataException($"Missing required property '{ProtocolPropertyName}'. Message content: {GetPayloadAsString()}");
} }
if (protocolVersion == null) if (protocolVersion == null)
{ {
throw new InvalidDataException($"Missing required property '{ProtocolVersionPropertyName}'."); throw new InvalidDataException($"Missing required property '{ProtocolVersionPropertyName}'. Message content: {GetPayloadAsString()}");
} }
requestMessage = new HandshakeRequestMessage(protocol, protocolVersion.Value); requestMessage = new HandshakeRequestMessage(protocol, protocolVersion.Value);
@ -249,6 +249,13 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
Utf8BufferTextReader.Return(textReader); Utf8BufferTextReader.Return(textReader);
} }
// For error messages, we want to print the payload as text
string GetPayloadAsString()
{
// REVIEW: Should we show hex for binary charaters?
return Encoding.UTF8.GetString(payload.ToArray());
}
return true; return true;
} }
} }

View File

@ -61,10 +61,11 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
[InlineData("42\u001e", "Unexpected JSON Token Type 'Integer'. Expected a JSON Object.")] [InlineData("42\u001e", "Unexpected JSON Token Type 'Integer'. Expected a JSON Object.")]
[InlineData("\"42\"\u001e", "Unexpected JSON Token Type 'String'. Expected a JSON Object.")] [InlineData("\"42\"\u001e", "Unexpected JSON Token Type 'String'. Expected a JSON Object.")]
[InlineData("null\u001e", "Unexpected JSON Token Type 'Null'. Expected a JSON Object.")] [InlineData("null\u001e", "Unexpected JSON Token Type 'Null'. Expected a JSON Object.")]
[InlineData("{}\u001e", "Missing required property 'protocol'.")] [InlineData("{}\u001e", "Missing required property 'protocol'. Message content: {}")]
[InlineData("[]\u001e", "Unexpected JSON Token Type 'Array'. Expected a JSON Object.")] [InlineData("[]\u001e", "Unexpected JSON Token Type 'Array'. Expected a JSON Object.")]
[InlineData("{\"protocol\":\"json\"}\u001e", "Missing required property 'version'.")] [InlineData("{\"protocol\":\"json\"}\u001e", "Missing required property 'version'. Message content: {\"protocol\":\"json\"}")]
[InlineData("{\"version\":1}\u001e", "Missing required property 'protocol'.")] [InlineData("{\"version\":1}\u001e", "Missing required property 'protocol'. Message content: {\"version\":1}")]
[InlineData("{\"type\":4,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":{}}\u001e", "Missing required property 'protocol'. Message content: {\"type\":4,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":{}}")]
[InlineData("{\"version\":\"123\"}\u001e", "Expected 'version' to be of type Integer.")] [InlineData("{\"version\":\"123\"}\u001e", "Expected 'version' to be of type Integer.")]
[InlineData("{\"protocol\":null,\"version\":123}\u001e", "Expected 'protocol' to be of type String.")] [InlineData("{\"protocol\":null,\"version\":123}\u001e", "Expected 'protocol' to be of type String.")]
public void ParsingHandshakeRequestMessageThrowsForInvalidMessages(string payload, string expectedMessage) public void ParsingHandshakeRequestMessageThrowsForInvalidMessages(string payload, string expectedMessage)