diff --git a/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/DefaultHubDispatcherBenchmark.cs b/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/DefaultHubDispatcherBenchmark.cs index fe86df6f6c..bc0e2e57df 100644 --- a/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/DefaultHubDispatcherBenchmark.cs +++ b/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/DefaultHubDispatcherBenchmark.cs @@ -166,67 +166,67 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks [Benchmark] public Task Invocation() { - return _dispatcher.DispatchMessageAsync(_connectionContext, new InvocationMessage("123", "Invocation", null, Array.Empty())); + return _dispatcher.DispatchMessageAsync(_connectionContext, new InvocationMessage("123", "Invocation", Array.Empty())); } [Benchmark] public Task InvocationAsync() { - return _dispatcher.DispatchMessageAsync(_connectionContext, new InvocationMessage("123", "InvocationAsync", null, Array.Empty())); + return _dispatcher.DispatchMessageAsync(_connectionContext, new InvocationMessage("123", "InvocationAsync", Array.Empty())); } [Benchmark] public Task InvocationReturnValue() { - return _dispatcher.DispatchMessageAsync(_connectionContext, new InvocationMessage("123", "InvocationReturnValue", null, Array.Empty())); + return _dispatcher.DispatchMessageAsync(_connectionContext, new InvocationMessage("123", "InvocationReturnValue", Array.Empty())); } [Benchmark] public Task InvocationReturnAsync() { - return _dispatcher.DispatchMessageAsync(_connectionContext, new InvocationMessage("123", "InvocationReturnAsync", null, Array.Empty())); + return _dispatcher.DispatchMessageAsync(_connectionContext, new InvocationMessage("123", "InvocationReturnAsync", Array.Empty())); } [Benchmark] public Task InvocationValueTaskAsync() { - return _dispatcher.DispatchMessageAsync(_connectionContext, new InvocationMessage("123", "InvocationValueTaskAsync", null, Array.Empty())); + return _dispatcher.DispatchMessageAsync(_connectionContext, new InvocationMessage("123", "InvocationValueTaskAsync", Array.Empty())); } [Benchmark] public Task StreamChannelReader() { - return _dispatcher.DispatchMessageAsync(_connectionContext, new StreamInvocationMessage("123", "StreamChannelReader", null, Array.Empty())); + return _dispatcher.DispatchMessageAsync(_connectionContext, new StreamInvocationMessage("123", "StreamChannelReader", Array.Empty())); } [Benchmark] public Task StreamChannelReaderAsync() { - return _dispatcher.DispatchMessageAsync(_connectionContext, new StreamInvocationMessage("123", "StreamChannelReaderAsync", null, Array.Empty())); + return _dispatcher.DispatchMessageAsync(_connectionContext, new StreamInvocationMessage("123", "StreamChannelReaderAsync", Array.Empty())); } [Benchmark] public Task StreamChannelReaderValueTaskAsync() { - return _dispatcher.DispatchMessageAsync(_connectionContext, new StreamInvocationMessage("123", "StreamChannelReaderValueTaskAsync", null, Array.Empty())); + return _dispatcher.DispatchMessageAsync(_connectionContext, new StreamInvocationMessage("123", "StreamChannelReaderValueTaskAsync", Array.Empty())); } [Benchmark] public Task StreamChannelReaderCount_Zero() { - return _dispatcher.DispatchMessageAsync(_connectionContext, new StreamInvocationMessage("123", "StreamChannelReaderCount", null, new object[] { 0 })); + return _dispatcher.DispatchMessageAsync(_connectionContext, new StreamInvocationMessage("123", "StreamChannelReaderCount", new object[] { 0 })); } [Benchmark] public Task StreamChannelReaderCount_One() { - return _dispatcher.DispatchMessageAsync(_connectionContext, new StreamInvocationMessage("123", "StreamChannelReaderCount", null, new object[] { 1 })); + return _dispatcher.DispatchMessageAsync(_connectionContext, new StreamInvocationMessage("123", "StreamChannelReaderCount", new object[] { 1 })); } [Benchmark] public Task StreamChannelReaderCount_Thousand() { - return _dispatcher.DispatchMessageAsync(_connectionContext, new StreamInvocationMessage("123", "StreamChannelReaderCount", null, new object[] { 1000 })); + return _dispatcher.DispatchMessageAsync(_connectionContext, new StreamInvocationMessage("123", "StreamChannelReaderCount", new object[] { 1000 })); } } } diff --git a/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/HubProtocolBenchmark.cs b/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/HubProtocolBenchmark.cs index 08ad64bc70..5c089c0f7a 100644 --- a/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/HubProtocolBenchmark.cs +++ b/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/HubProtocolBenchmark.cs @@ -37,16 +37,16 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks switch (Input) { case Message.NoArguments: - _hubMessage = new InvocationMessage("Target", null, Array.Empty()); + _hubMessage = new InvocationMessage("Target", Array.Empty()); break; case Message.FewArguments: - _hubMessage = new InvocationMessage("Target", null, new object[] { 1, "Foo", 2.0f }); + _hubMessage = new InvocationMessage("Target", new object[] { 1, "Foo", 2.0f }); break; case Message.ManyArguments: - _hubMessage = new InvocationMessage("Target", null, new object[] { 1, "string", 2.0f, true, (byte)9, new byte[] { 5, 4, 3, 2, 1 }, 'c', 123456789101112L }); + _hubMessage = new InvocationMessage("Target", new object[] { 1, "string", 2.0f, true, (byte)9, new byte[] { 5, 4, 3, 2, 1 }, 'c', 123456789101112L }); break; case Message.LargeArguments: - _hubMessage = new InvocationMessage("Target", null, new object[] { new string('F', 10240), new byte[10240] }); + _hubMessage = new InvocationMessage("Target", new object[] { new string('F', 10240), new byte[10240] }); break; } diff --git a/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/ServerSentEventsBenchmark.cs b/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/ServerSentEventsBenchmark.cs index 825992edac..33b9055428 100644 --- a/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/ServerSentEventsBenchmark.cs +++ b/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/ServerSentEventsBenchmark.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Buffers; using System.IO; using System.Threading.Tasks; @@ -44,16 +44,16 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks switch (Input) { case Message.NoArguments: - hubMessage = new InvocationMessage("Target", null, Array.Empty()); + hubMessage = new InvocationMessage("Target", Array.Empty()); break; case Message.FewArguments: - hubMessage = new InvocationMessage("Target", null, new object[] { 1, "Foo", 2.0f }); + hubMessage = new InvocationMessage("Target", new object[] { 1, "Foo", 2.0f }); break; case Message.ManyArguments: - hubMessage = new InvocationMessage("Target", null, new object[] { 1, "string", 2.0f, true, (byte)9, new[] { 5, 4, 3, 2, 1 }, 'c', 123456789101112L }); + hubMessage = new InvocationMessage("Target", new object[] { 1, "string", 2.0f, true, (byte)9, new[] { 5, 4, 3, 2, 1 }, 'c', 123456789101112L }); break; case Message.LargeArguments: - hubMessage = new InvocationMessage("Target", null, new object[] { new string('F', 10240), new string('B', 10240) }); + hubMessage = new InvocationMessage("Target", new object[] { new string('F', 10240), new string('B', 10240) }); break; } diff --git a/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnection.Log.cs b/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnection.Log.cs index b17ae06043..5dbcf2985e 100644 --- a/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnection.Log.cs +++ b/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnection.Log.cs @@ -168,6 +168,9 @@ namespace Microsoft.AspNetCore.SignalR.Client private static readonly Action _processingMessage = LoggerMessage.Define(LogLevel.Debug, new EventId(56, "ProcessingMessage"), "Processing {MessageLength} byte message from server."); + private static readonly Action _argumentBindingFailure = + LoggerMessage.Define(LogLevel.Error, new EventId(57, "ArgumentBindingFailure"), "Failed to bind arguments received in invocation '{InvocationId}' of '{MethodName}'."); + public static void PreparingNonBlockingInvocation(ILogger logger, string target, int count) { _preparingNonBlockingInvocation(logger, target, count, null); @@ -444,6 +447,11 @@ namespace Microsoft.AspNetCore.SignalR.Client { _unableToSendCancellation(logger, invocationId, null); } + + public static void ArgumentBindingFailure(ILogger logger, string invocationId, string target, Exception exception) + { + _argumentBindingFailure(logger, invocationId, target, exception); + } } } } diff --git a/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnection.cs b/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnection.cs index 44d6bdb969..da41d807a2 100644 --- a/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnection.cs +++ b/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnection.cs @@ -300,7 +300,7 @@ namespace Microsoft.AspNetCore.SignalR.Client Log.PreparingBlockingInvocation(_logger, irq.InvocationId, methodName, irq.ResultType.FullName, args.Length); // Client invocations are always blocking - var invocationMessage = new InvocationMessage(irq.InvocationId, methodName, null, args); + var invocationMessage = new InvocationMessage(irq.InvocationId, methodName, args); Log.RegisteringInvocation(_logger, invocationMessage.InvocationId); @@ -327,7 +327,7 @@ namespace Microsoft.AspNetCore.SignalR.Client Log.PreparingStreamingInvocation(_logger, irq.InvocationId, methodName, irq.ResultType.FullName, args.Length); - var invocationMessage = new StreamInvocationMessage(irq.InvocationId, methodName, null, args); + var invocationMessage = new StreamInvocationMessage(irq.InvocationId, methodName, args); // I just want an excuse to use 'irq' as a variable name... Log.RegisteringInvocation(_logger, invocationMessage.InvocationId); @@ -375,7 +375,7 @@ namespace Microsoft.AspNetCore.SignalR.Client Log.PreparingNonBlockingInvocation(_logger, methodName, args.Length); - var invocationMessage = new InvocationMessage(null, methodName, null, args); + var invocationMessage = new InvocationMessage(null, methodName, args); await SendHubMessage(invocationMessage, cancellationToken); } @@ -390,9 +390,13 @@ namespace Microsoft.AspNetCore.SignalR.Client InvocationRequest irq; switch (message) { + case InvocationBindingFailureMessage bindingFailure: + // The server can't receive a response, so we just drop the message and log + // REVIEW: Is this the right approach? + Log.ArgumentBindingFailure(_logger, bindingFailure.InvocationId, bindingFailure.Target, bindingFailure.BindingFailure.SourceException); + break; case InvocationMessage invocation: - Log.ReceivedInvocation(_logger, invocation.InvocationId, invocation.Target, - invocation.ArgumentBindingException != null ? null : invocation.Arguments); + Log.ReceivedInvocation(_logger, invocation.InvocationId, invocation.Target, invocation.Arguments); await DispatchInvocationAsync(invocation); break; case CompletionMessage completion: diff --git a/src/Microsoft.AspNetCore.SignalR.Common/Protocol/HubMethodInvocationMessage.cs b/src/Microsoft.AspNetCore.SignalR.Common/Protocol/HubMethodInvocationMessage.cs index f125981950..272e446d0b 100644 --- a/src/Microsoft.AspNetCore.SignalR.Common/Protocol/HubMethodInvocationMessage.cs +++ b/src/Microsoft.AspNetCore.SignalR.Common/Protocol/HubMethodInvocationMessage.cs @@ -3,33 +3,16 @@ using System; using System.Linq; -using System.Runtime.ExceptionServices; namespace Microsoft.AspNetCore.SignalR.Protocol { public abstract class HubMethodInvocationMessage : HubInvocationMessage { - private readonly ExceptionDispatchInfo _argumentBindingException; - private readonly object[] _arguments; - public string Target { get; } - public object[] Arguments - { - get - { - if (_argumentBindingException != null) - { - _argumentBindingException.Throw(); - } + public object[] Arguments { get; } - return _arguments; - } - } - - public Exception ArgumentBindingException => _argumentBindingException?.SourceException; - - protected HubMethodInvocationMessage(string invocationId, string target, ExceptionDispatchInfo argumentBindingException, object[] arguments) + protected HubMethodInvocationMessage(string invocationId, string target, object[] arguments) : base(invocationId) { if (string.IsNullOrEmpty(target)) @@ -37,26 +20,20 @@ namespace Microsoft.AspNetCore.SignalR.Protocol throw new ArgumentNullException(nameof(target)); } - if ((arguments == null && argumentBindingException == null) || (arguments?.Length > 0 && argumentBindingException != null)) - { - throw new ArgumentException($"'{nameof(argumentBindingException)}' and '{nameof(arguments)}' are mutually exclusive"); - } - Target = target; - _arguments = arguments; - _argumentBindingException = argumentBindingException; + Arguments = arguments; } } public class InvocationMessage : HubMethodInvocationMessage { - public InvocationMessage(string target, ExceptionDispatchInfo argumentBindingException, object[] arguments) - : this(null, target, argumentBindingException, arguments) + public InvocationMessage(string target, object[] arguments) + : this(null, target, arguments) { } - public InvocationMessage(string invocationId, string target, ExceptionDispatchInfo argumentBindingException, object[] arguments) - : base(invocationId, target, argumentBindingException, arguments) + public InvocationMessage(string invocationId, string target, object[] arguments) + : base(invocationId, target, arguments) { } @@ -77,8 +54,8 @@ namespace Microsoft.AspNetCore.SignalR.Protocol public class StreamInvocationMessage : HubMethodInvocationMessage { - public StreamInvocationMessage(string invocationId, string target, ExceptionDispatchInfo argumentBindingException, object[] arguments) - : base(invocationId, target, argumentBindingException, arguments) + public StreamInvocationMessage(string invocationId, string target, object[] arguments) + : base(invocationId, target, arguments) { if (string.IsNullOrEmpty(invocationId)) { diff --git a/src/Microsoft.AspNetCore.SignalR.Common/Protocol/InvocationBindingFailureMessage.cs b/src/Microsoft.AspNetCore.SignalR.Common/Protocol/InvocationBindingFailureMessage.cs new file mode 100644 index 0000000000..74c88ddc85 --- /dev/null +++ b/src/Microsoft.AspNetCore.SignalR.Common/Protocol/InvocationBindingFailureMessage.cs @@ -0,0 +1,22 @@ +using System.Runtime.ExceptionServices; + +namespace Microsoft.AspNetCore.SignalR.Protocol +{ + /// + /// Represents a failure to bind arguments for an invocation. This does not represent an actual + /// message that is sent on the wire, it is returned by + /// to indicate that a binding failure occurred when parsing an invocation. The invocation ID is associated + /// so that the error can be sent back to the client, associated with the appropriate invocation ID. + /// + public class InvocationBindingFailureMessage : HubInvocationMessage + { + public ExceptionDispatchInfo BindingFailure { get; } + public string Target { get; } + + public InvocationBindingFailureMessage(string invocationId, string target, ExceptionDispatchInfo bindingFailure) : base(invocationId) + { + Target = target; + BindingFailure = bindingFailure; + } + } +} diff --git a/src/Microsoft.AspNetCore.SignalR.Core/DefaultHubLifetimeManager.cs b/src/Microsoft.AspNetCore.SignalR.Core/DefaultHubLifetimeManager.cs index 4a5da0f4e0..c6cb42e36c 100644 --- a/src/Microsoft.AspNetCore.SignalR.Core/DefaultHubLifetimeManager.cs +++ b/src/Microsoft.AspNetCore.SignalR.Core/DefaultHubLifetimeManager.cs @@ -250,7 +250,7 @@ namespace Microsoft.AspNetCore.SignalR private HubMessage CreateInvocationMessage(string methodName, object[] args) { - return new InvocationMessage(methodName, null, args); + return new InvocationMessage(methodName, args); } public override Task SendUserAsync(string userId, string methodName, object[] args) diff --git a/src/Microsoft.AspNetCore.SignalR.Core/Internal/DefaultHubDispatcher.cs b/src/Microsoft.AspNetCore.SignalR.Core/Internal/DefaultHubDispatcher.cs index 748f345459..66046ebaa5 100644 --- a/src/Microsoft.AspNetCore.SignalR.Core/Internal/DefaultHubDispatcher.cs +++ b/src/Microsoft.AspNetCore.SignalR.Core/Internal/DefaultHubDispatcher.cs @@ -77,6 +77,10 @@ namespace Microsoft.AspNetCore.SignalR.Internal { switch (hubMessage) { + case InvocationBindingFailureMessage bindingFailureMessage: + await ProcessBindingFailure(connection, bindingFailureMessage); + break; + case InvocationMessage invocationMessage: Log.ReceivedHubInvocation(_logger, invocationMessage); await ProcessInvocation(connection, invocationMessage, isStreamedInvocation: false); @@ -113,6 +117,14 @@ namespace Microsoft.AspNetCore.SignalR.Internal } } + private Task ProcessBindingFailure(HubConnectionContext connection, InvocationBindingFailureMessage bindingFailureMessage) + { + Log.FailedInvokingHubMethod(_logger, bindingFailureMessage.Target, bindingFailureMessage.BindingFailure.SourceException); + var errorMessage = ErrorMessageHelper.BuildErrorMessage($"Failed to invoke '{bindingFailureMessage.Target}' due to an error on the server.", + bindingFailureMessage.BindingFailure.SourceException, _enableDetailedErrors); + return SendInvocationError(bindingFailureMessage.InvocationId, connection, errorMessage); + } + public override Type GetReturnType(string invocationId) { return typeof(object); @@ -163,7 +175,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal if (!await IsHubMethodAuthorized(scope.ServiceProvider, connection.User, descriptor.Policies)) { Log.HubMethodNotAuthorized(_logger, hubMethodInvocationMessage.Target); - await SendInvocationError(hubMethodInvocationMessage, connection, + await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection, $"Failed to invoke '{hubMethodInvocationMessage.Target}' because user is unauthorized"); return; } @@ -173,15 +185,6 @@ namespace Microsoft.AspNetCore.SignalR.Internal return; } - if (hubMethodInvocationMessage.ArgumentBindingException != null) - { - Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, hubMethodInvocationMessage.ArgumentBindingException); - var errorMessage = ErrorMessageHelper.BuildErrorMessage($"Failed to invoke '{hubMethodInvocationMessage.Target}' due to an error on the server.", - hubMethodInvocationMessage.ArgumentBindingException, _enableDetailedErrors); - await SendInvocationError(hubMethodInvocationMessage, connection, errorMessage); - return; - } - var hubActivator = scope.ServiceProvider.GetRequiredService>(); var hub = hubActivator.Create(); @@ -197,7 +200,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal { Log.InvalidReturnValueFromStreamingMethod(_logger, methodExecutor.MethodInfo.Name); - await SendInvocationError(hubMethodInvocationMessage, connection, + await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection, $"The value returned by the streaming method '{methodExecutor.MethodInfo.Name}' is not a ChannelReader<>."); return; } @@ -215,13 +218,13 @@ namespace Microsoft.AspNetCore.SignalR.Internal catch (TargetInvocationException ex) { Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex); - await SendInvocationError(hubMethodInvocationMessage, connection, + await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection, ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex.InnerException, _enableDetailedErrors)); } catch (Exception ex) { Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex); - await SendInvocationError(hubMethodInvocationMessage, connection, + await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection, ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex, _enableDetailedErrors)); } finally @@ -294,15 +297,15 @@ namespace Microsoft.AspNetCore.SignalR.Internal return null; } - private async Task SendInvocationError(HubMethodInvocationMessage hubMethodInvocationMessage, + private async Task SendInvocationError(string invocationId, HubConnectionContext connection, string errorMessage) { - if (string.IsNullOrEmpty(hubMethodInvocationMessage.InvocationId)) + if (string.IsNullOrEmpty(invocationId)) { return; } - await connection.WriteAsync(CompletionMessage.WithError(hubMethodInvocationMessage.InvocationId, errorMessage)); + await connection.WriteAsync(CompletionMessage.WithError(invocationId, errorMessage)); } private void InitializeHub(THub hub, HubConnectionContext connection) diff --git a/src/Microsoft.AspNetCore.SignalR.Protocols.Json/Protocol/JsonHubProtocol.cs b/src/Microsoft.AspNetCore.SignalR.Protocols.Json/Protocol/JsonHubProtocol.cs index 919d89399f..4c471386a7 100644 --- a/src/Microsoft.AspNetCore.SignalR.Protocols.Json/Protocol/JsonHubProtocol.cs +++ b/src/Microsoft.AspNetCore.SignalR.Protocols.Json/Protocol/JsonHubProtocol.cs @@ -237,6 +237,7 @@ namespace Microsoft.AspNetCore.SignalR.Protocol { if (argumentsToken != null) { + // We weren't able to bind the arguments because they came before the 'target', so try to bind now that we've read everything. try { var paramTypes = binder.GetParameterTypes(target); @@ -248,13 +249,16 @@ namespace Microsoft.AspNetCore.SignalR.Protocol } } - message = BindInvocationMessage(invocationId, target, argumentBindingException, arguments, hasArguments, binder); + message = argumentBindingException != null + ? new InvocationBindingFailureMessage(invocationId, target, argumentBindingException) + : BindInvocationMessage(invocationId, target, arguments, hasArguments, binder); } break; case HubProtocolConstants.StreamInvocationMessageType: { if (argumentsToken != null) { + // We weren't able to bind the arguments because they came before the 'target', so try to bind now that we've read everything. try { var paramTypes = binder.GetParameterTypes(target); @@ -266,7 +270,9 @@ namespace Microsoft.AspNetCore.SignalR.Protocol } } - message = BindStreamInvocationMessage(invocationId, target, argumentBindingException, arguments, hasArguments, binder); + message = argumentBindingException != null + ? new InvocationBindingFailureMessage(invocationId, target, argumentBindingException) + : BindStreamInvocationMessage(invocationId, target, arguments, hasArguments, binder); } break; case HubProtocolConstants.StreamItemMessageType: @@ -539,7 +545,7 @@ namespace Microsoft.AspNetCore.SignalR.Protocol return new StreamItemMessage(invocationId, item); } - private HubMessage BindStreamInvocationMessage(string invocationId, string target, ExceptionDispatchInfo argumentBindingException, object[] arguments, bool hasArguments, IInvocationBinder binder) + private HubMessage BindStreamInvocationMessage(string invocationId, string target, object[] arguments, bool hasArguments, IInvocationBinder binder) { if (string.IsNullOrEmpty(invocationId)) { @@ -556,10 +562,10 @@ namespace Microsoft.AspNetCore.SignalR.Protocol throw new InvalidDataException($"Missing required property '{TargetPropertyName}'."); } - return new StreamInvocationMessage(invocationId, target, argumentBindingException, arguments); + return new StreamInvocationMessage(invocationId, target, arguments); } - private HubMessage BindInvocationMessage(string invocationId, string target, ExceptionDispatchInfo argumentBindingException, object[] arguments, bool hasArguments, IInvocationBinder binder) + private HubMessage BindInvocationMessage(string invocationId, string target, object[] arguments, bool hasArguments, IInvocationBinder binder) { if (string.IsNullOrEmpty(target)) { @@ -571,7 +577,7 @@ namespace Microsoft.AspNetCore.SignalR.Protocol throw new InvalidDataException($"Missing required property '{ArgumentsPropertyName}'."); } - return new InvocationMessage(invocationId, target, argumentBindingException, arguments); + return new InvocationMessage(invocationId, target, arguments); } private object[] BindArguments(JsonTextReader reader, IReadOnlyList paramTypes) diff --git a/src/Microsoft.AspNetCore.SignalR.Protocols.MessagePack/Protocol/MessagePackHubProtocol.cs b/src/Microsoft.AspNetCore.SignalR.Protocols.MessagePack/Protocol/MessagePackHubProtocol.cs index cc8009045a..02a8065def 100644 --- a/src/Microsoft.AspNetCore.SignalR.Protocols.MessagePack/Protocol/MessagePackHubProtocol.cs +++ b/src/Microsoft.AspNetCore.SignalR.Protocols.MessagePack/Protocol/MessagePackHubProtocol.cs @@ -129,7 +129,7 @@ namespace Microsoft.AspNetCore.SignalR.Protocol } } - private static InvocationMessage CreateInvocationMessage(byte[] input, ref int offset, IInvocationBinder binder, IFormatterResolver resolver) + private static HubMessage CreateInvocationMessage(byte[] input, ref int offset, IInvocationBinder binder, IFormatterResolver resolver) { var headers = ReadHeaders(input, ref offset); var invocationId = ReadInvocationId(input, ref offset); @@ -147,15 +147,15 @@ namespace Microsoft.AspNetCore.SignalR.Protocol try { var arguments = BindArguments(input, ref offset, parameterTypes, resolver); - return ApplyHeaders(headers, new InvocationMessage(invocationId, target, null, arguments)); + return ApplyHeaders(headers, new InvocationMessage(invocationId, target, arguments)); } catch (Exception ex) { - return ApplyHeaders(headers, new InvocationMessage(invocationId, target, ExceptionDispatchInfo.Capture(ex), null)); + return new InvocationBindingFailureMessage(invocationId, target, ExceptionDispatchInfo.Capture(ex)); } } - private static StreamInvocationMessage CreateStreamInvocationMessage(byte[] input, ref int offset, IInvocationBinder binder, IFormatterResolver resolver) + private static HubMessage CreateStreamInvocationMessage(byte[] input, ref int offset, IInvocationBinder binder, IFormatterResolver resolver) { var headers = ReadHeaders(input, ref offset); var invocationId = ReadInvocationId(input, ref offset); @@ -165,11 +165,11 @@ namespace Microsoft.AspNetCore.SignalR.Protocol try { var arguments = BindArguments(input, ref offset, parameterTypes, resolver); - return ApplyHeaders(headers, new StreamInvocationMessage(invocationId, target, null, arguments)); + return ApplyHeaders(headers, new StreamInvocationMessage(invocationId, target, arguments)); } catch (Exception ex) { - return ApplyHeaders(headers, new StreamInvocationMessage(invocationId, target, ExceptionDispatchInfo.Capture(ex), null)); + return new InvocationBindingFailureMessage(invocationId, target, ExceptionDispatchInfo.Capture(ex)); } } diff --git a/src/Microsoft.AspNetCore.SignalR.Redis/Internal/RedisProtocol.cs b/src/Microsoft.AspNetCore.SignalR.Redis/Internal/RedisProtocol.cs index 98dcd8955f..6d3c51659b 100644 --- a/src/Microsoft.AspNetCore.SignalR.Redis/Internal/RedisProtocol.cs +++ b/src/Microsoft.AspNetCore.SignalR.Redis/Internal/RedisProtocol.cs @@ -62,7 +62,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Internal } WriteSerializedHubMessage(writer, - new SerializedHubMessage(new InvocationMessage(methodName, null, args))); + new SerializedHubMessage(new InvocationMessage(methodName, args))); return writer.ToArray(); } finally diff --git a/src/Microsoft.AspNetCore.SignalR.Redis/RedisHubLifetimeManager.cs b/src/Microsoft.AspNetCore.SignalR.Redis/RedisHubLifetimeManager.cs index 6c471ce7f8..174abbebc5 100644 --- a/src/Microsoft.AspNetCore.SignalR.Redis/RedisHubLifetimeManager.cs +++ b/src/Microsoft.AspNetCore.SignalR.Redis/RedisHubLifetimeManager.cs @@ -130,7 +130,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis var connection = _connections[connectionId]; if (connection != null) { - return connection.WriteAsync(new InvocationMessage(methodName, null, args)).AsTask(); + return connection.WriteAsync(new InvocationMessage(methodName, args)).AsTask(); } var message = _protocol.WriteInvocation(methodName, args); diff --git a/test/Microsoft.AspNetCore.SignalR.Common.Tests/Internal/Protocol/JsonHubProtocolTests.cs b/test/Microsoft.AspNetCore.SignalR.Common.Tests/Internal/Protocol/JsonHubProtocolTests.cs index 18fea62dd5..c30209ccce 100644 --- a/test/Microsoft.AspNetCore.SignalR.Common.Tests/Internal/Protocol/JsonHubProtocolTests.cs +++ b/test/Microsoft.AspNetCore.SignalR.Common.Tests/Internal/Protocol/JsonHubProtocolTests.cs @@ -32,17 +32,17 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol public static IDictionary ProtocolTestData => new[] { - new JsonProtocolTestData("InvocationMessage_HasInvocationId", new InvocationMessage("123", "Target", null, new object[] { 1, "Foo", 2.0f }), true, NullValueHandling.Ignore, "{\"type\":1,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"), - new JsonProtocolTestData("InvocationMessage_HasFloatArgument", new InvocationMessage(null, "Target", null, new object[] { 1, "Foo", 2.0f }), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"), - new JsonProtocolTestData("InvocationMessage_HasBoolArgument", new InvocationMessage(null, "Target", null, new object[] { true }), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[true]}"), - new JsonProtocolTestData("InvocationMessage_HasNullArgument", new InvocationMessage(null, "Target", null, new object[] { null }), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[null]}"), - new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNoCamelCase", new InvocationMessage(null, "Target", null, new object[] { new CustomObject() }), false, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}]}"), - new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueIgnore", new InvocationMessage(null, "Target", null, new object[] { new CustomObject() }), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}]}"), - new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueIgnoreAndNoCamelCase", new InvocationMessage(null, "Target", null, new object[] { new CustomObject() }), false, NullValueHandling.Include, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}"), - new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueInclude", new InvocationMessage(null, "Target", null, new object[] { new CustomObject() }), true, NullValueHandling.Include, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"), - new JsonProtocolTestData("InvocationMessage_HasHeaders", AddHeaders(TestHeaders, new InvocationMessage("123", "Target", null, new object[] { 1, "Foo", 2.0f })), true, NullValueHandling.Ignore, "{\"type\":1," + SerializedHeaders + ",\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"), - new JsonProtocolTestData("InvocationMessage_StringIsoDateArgument", new InvocationMessage("Method", null, new object[] { "2016-05-10T13:51:20+12:34" }), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20+12:34\"]}"), - new JsonProtocolTestData("InvocationMessage_DateTimeOffsetArgument", new InvocationMessage("Method", null, new object[] { DateTimeOffset.Parse("2016-05-10T13:51:20+12:34") }), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20+12:34\"]}"), + new JsonProtocolTestData("InvocationMessage_HasInvocationId", new InvocationMessage("123", "Target", new object[] { 1, "Foo", 2.0f }), true, NullValueHandling.Ignore, "{\"type\":1,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"), + new JsonProtocolTestData("InvocationMessage_HasFloatArgument", new InvocationMessage(null, "Target", new object[] { 1, "Foo", 2.0f }), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"), + new JsonProtocolTestData("InvocationMessage_HasBoolArgument", new InvocationMessage(null, "Target", new object[] { true }), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[true]}"), + new JsonProtocolTestData("InvocationMessage_HasNullArgument", new InvocationMessage(null, "Target", new object[] { null }), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[null]}"), + new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNoCamelCase", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), false, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}]}"), + new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueIgnore", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}]}"), + new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueIgnoreAndNoCamelCase", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), false, NullValueHandling.Include, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}"), + new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueInclude", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), true, NullValueHandling.Include, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"), + new JsonProtocolTestData("InvocationMessage_HasHeaders", AddHeaders(TestHeaders, new InvocationMessage("123", "Target", new object[] { 1, "Foo", 2.0f })), true, NullValueHandling.Ignore, "{\"type\":1," + SerializedHeaders + ",\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"), + new JsonProtocolTestData("InvocationMessage_StringIsoDateArgument", new InvocationMessage("Method", new object[] { "2016-05-10T13:51:20+12:34" }), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20+12:34\"]}"), + new JsonProtocolTestData("InvocationMessage_DateTimeOffsetArgument", new InvocationMessage("Method", new object[] { DateTimeOffset.Parse("2016-05-10T13:51:20+12:34") }), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20+12:34\"]}"), new JsonProtocolTestData("StreamItemMessage_HasIntegerItem", new StreamItemMessage("123", 1), true, NullValueHandling.Ignore, "{\"type\":2,\"invocationId\":\"123\",\"item\":1}"), new JsonProtocolTestData("StreamItemMessage_HasStringItem", new StreamItemMessage("123", "Foo"), true, NullValueHandling.Ignore, "{\"type\":2,\"invocationId\":\"123\",\"item\":\"Foo\"}"), @@ -70,15 +70,15 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol new JsonProtocolTestData("CompletionMessage_HasErrorAndCamelCase", CompletionMessage.Empty("123"), true, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\"}"), new JsonProtocolTestData("CompletionMessage_HasErrorAndHeadersAndCamelCase", AddHeaders(TestHeaders, CompletionMessage.Empty("123")), true, NullValueHandling.Ignore, "{\"type\":3," + SerializedHeaders + ",\"invocationId\":\"123\"}"), - new JsonProtocolTestData("StreamInvocationMessage_HasInvocationId", new StreamInvocationMessage("123", "Target", null, new object[] { 1, "Foo", 2.0f }), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"), - new JsonProtocolTestData("StreamInvocationMessage_HasFloatArgument", new StreamInvocationMessage("123", "Target", null, new object[] { 1, "Foo", 2.0f }), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"), - new JsonProtocolTestData("StreamInvocationMessage_HasBoolArgument", new StreamInvocationMessage("123", "Target", null, new object[] { true }), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[true]}"), - new JsonProtocolTestData("StreamInvocationMessage_HasNullArgument", new StreamInvocationMessage("123", "Target", null, new object[] { null }), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[null]}"), - new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNoCamelCase", new StreamInvocationMessage("123", "Target", null, new object[] { new CustomObject() }), false, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}]}"), - new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueIgnore", new StreamInvocationMessage("123", "Target", null, new object[] { new CustomObject() }), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}]}"), - new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueIgnoreAndNoCamelCase", new StreamInvocationMessage("123", "Target", null, new object[] { new CustomObject() }), false, NullValueHandling.Include, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}"), - new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueInclude", new StreamInvocationMessage("123", "Target", null, new object[] { new CustomObject() }), true, NullValueHandling.Include, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"), - new JsonProtocolTestData("StreamInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new StreamInvocationMessage("123", "Target", null, new object[] { new CustomObject() })), true, NullValueHandling.Include, "{\"type\":4," + SerializedHeaders + ",\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"), + new JsonProtocolTestData("StreamInvocationMessage_HasInvocationId", new StreamInvocationMessage("123", "Target", new object[] { 1, "Foo", 2.0f }), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"), + new JsonProtocolTestData("StreamInvocationMessage_HasFloatArgument", new StreamInvocationMessage("123", "Target", new object[] { 1, "Foo", 2.0f }), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"), + new JsonProtocolTestData("StreamInvocationMessage_HasBoolArgument", new StreamInvocationMessage("123", "Target", new object[] { true }), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[true]}"), + new JsonProtocolTestData("StreamInvocationMessage_HasNullArgument", new StreamInvocationMessage("123", "Target", new object[] { null }), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[null]}"), + new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNoCamelCase", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), false, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}]}"), + new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueIgnore", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}]}"), + new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueIgnoreAndNoCamelCase", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), false, NullValueHandling.Include, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}"), + new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueInclude", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), true, NullValueHandling.Include, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"), + new JsonProtocolTestData("StreamInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() })), true, NullValueHandling.Include, "{\"type\":4," + SerializedHeaders + ",\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"), new JsonProtocolTestData("CancelInvocationMessage_HasInvocationId", new CancelInvocationMessage("123"), true, NullValueHandling.Ignore, "{\"type\":5,\"invocationId\":\"123\"}"), new JsonProtocolTestData("CancelInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new CancelInvocationMessage("123")), true, NullValueHandling.Ignore, "{\"type\":5," + SerializedHeaders + ",\"invocationId\":\"123\"}"), @@ -95,10 +95,10 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol public static IDictionary OutOfOrderJsonTestData => new[] { - new JsonProtocolTestData("InvocationMessage_StringIsoDateArgumentFirst", new InvocationMessage("Method", null, new object[] { "2016-05-10T13:51:20+12:34" }), false, NullValueHandling.Ignore, "{ \"arguments\": [\"2016-05-10T13:51:20+12:34\"], \"type\":1, \"target\": \"Method\" }"), - new JsonProtocolTestData("InvocationMessage_DateTimeOffsetArgumentFirst", new InvocationMessage("Method", null, new object[] { DateTimeOffset.Parse("2016-05-10T13:51:20+12:34") }), false, NullValueHandling.Ignore, "{ \"arguments\": [\"2016-05-10T13:51:20+12:34\"], \"type\":1, \"target\": \"Method\" }"), - new JsonProtocolTestData("InvocationMessage_IntegerArrayArgumentFirst", new InvocationMessage("Method", null, new object[] { 1, 2 }), false, NullValueHandling.Ignore, "{ \"arguments\": [1,2], \"type\":1, \"target\": \"Method\" }"), - new JsonProtocolTestData("StreamInvocationMessage_IntegerArrayArgumentFirst", new StreamInvocationMessage("3", "Method", null, new object[] { 1, 2 }), false, NullValueHandling.Ignore, "{ \"type\":4, \"arguments\": [1,2], \"target\": \"Method\", \"invocationId\": \"3\" }"), + new JsonProtocolTestData("InvocationMessage_StringIsoDateArgumentFirst", new InvocationMessage("Method", new object[] { "2016-05-10T13:51:20+12:34" }), false, NullValueHandling.Ignore, "{ \"arguments\": [\"2016-05-10T13:51:20+12:34\"], \"type\":1, \"target\": \"Method\" }"), + new JsonProtocolTestData("InvocationMessage_DateTimeOffsetArgumentFirst", new InvocationMessage("Method", new object[] { DateTimeOffset.Parse("2016-05-10T13:51:20+12:34") }), false, NullValueHandling.Ignore, "{ \"arguments\": [\"2016-05-10T13:51:20+12:34\"], \"type\":1, \"target\": \"Method\" }"), + new JsonProtocolTestData("InvocationMessage_IntegerArrayArgumentFirst", new InvocationMessage("Method", new object[] { 1, 2 }), false, NullValueHandling.Ignore, "{ \"arguments\": [1,2], \"type\":1, \"target\": \"Method\" }"), + new JsonProtocolTestData("StreamInvocationMessage_IntegerArrayArgumentFirst", new StreamInvocationMessage("3", "Method", new object[] { 1, 2 }), false, NullValueHandling.Ignore, "{ \"type\":4, \"arguments\": [1,2], \"target\": \"Method\", \"invocationId\": \"3\" }"), new JsonProtocolTestData("CompletionMessage_ResultFirst", new CompletionMessage("15", null, 10, hasResult: true), false, NullValueHandling.Ignore, "{ \"type\":3, \"result\": 10, \"invocationId\": \"15\" }"), new JsonProtocolTestData("StreamItemMessage_ItemFirst", new StreamItemMessage("1a", "foo"), false, NullValueHandling.Ignore, "{ \"item\": \"foo\", \"invocationId\": \"1a\", \"type\":2 }") }.ToDictionary(t => t.Name); @@ -256,8 +256,8 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol var protocol = new JsonHubProtocol(); var data = new ReadOnlySequence(Encoding.UTF8.GetBytes(input)); protocol.TryParseMessage(ref data, binder, out var message); - var ex = Assert.Throws(() => ((HubMethodInvocationMessage)message).Arguments); - Assert.Equal(expectedMessage, ex.Message); + var bindingFailure = Assert.IsType(message); + Assert.Equal(expectedMessage, bindingFailure.BindingFailure.SourceException.Message); } private static string Frame(string input) diff --git a/test/Microsoft.AspNetCore.SignalR.Common.Tests/Internal/Protocol/MessagePackHubProtocolTests.cs b/test/Microsoft.AspNetCore.SignalR.Common.Tests/Internal/Protocol/MessagePackHubProtocolTests.cs index dcfe2b23a1..c0fbec1baf 100644 --- a/test/Microsoft.AspNetCore.SignalR.Common.Tests/Internal/Protocol/MessagePackHubProtocolTests.cs +++ b/test/Microsoft.AspNetCore.SignalR.Common.Tests/Internal/Protocol/MessagePackHubProtocolTests.cs @@ -57,39 +57,39 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol // Invocation messages new ProtocolTestData( name: "InvocationWithNoHeadersAndNoArgs", - message: new InvocationMessage("xyz", "method", null, Array.Empty()), + message: new InvocationMessage("xyz", "method", Array.Empty()), binary: "lQGAo3h5eqZtZXRob2SQ"), new ProtocolTestData( name: "InvocationWithNoHeadersNoIdAndNoArgs", - message: new InvocationMessage("method", null, Array.Empty()), + message: new InvocationMessage("method", Array.Empty()), binary: "lQGAwKZtZXRob2SQ"), new ProtocolTestData( name: "InvocationWithNoHeadersNoIdAndSingleNullArg", - message: new InvocationMessage("method", null, new object[] { null }), + message: new InvocationMessage("method", new object[] { null }), binary: "lQGAwKZtZXRob2SRwA=="), new ProtocolTestData( name: "InvocationWithNoHeadersNoIdAndSingleIntArg", - message: new InvocationMessage("method", null, new object[] { 42 }), + message: new InvocationMessage("method", new object[] { 42 }), binary: "lQGAwKZtZXRob2SRKg=="), new ProtocolTestData( name: "InvocationWithNoHeadersNoIdIntAndStringArgs", - message: new InvocationMessage("method", null, new object[] { 42, "string" }), + message: new InvocationMessage("method", new object[] { 42, "string" }), binary: "lQGAwKZtZXRob2SSKqZzdHJpbmc="), new ProtocolTestData( name: "InvocationWithNoHeadersNoIdIntAndEnumArgs", - message: new InvocationMessage("method", null, new object[] { 42, TestEnum.One }), + message: new InvocationMessage("method", new object[] { 42, TestEnum.One }), binary: "lQGAwKZtZXRob2SSKqNPbmU="), new ProtocolTestData( name: "InvocationWithNoHeadersNoIdAndCustomObjectArg", - message: new InvocationMessage("method", null, new object[] { 42, "string", new CustomObject() }), + message: new InvocationMessage("method", new object[] { 42, "string", new CustomObject() }), binary: "lQGAwKZtZXRob2STKqZzdHJpbmeGqlN0cmluZ1Byb3CoU2lnbmFsUiGqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqxEYXRlVGltZVByb3DW/1jsHICoTnVsbFByb3DAq0J5dGVBcnJQcm9wxAMBAgM="), new ProtocolTestData( name: "InvocationWithNoHeadersNoIdAndArrayOfCustomObjectArgs", - message: new InvocationMessage("method", null, new object[] { new CustomObject(), new CustomObject() }), + message: new InvocationMessage("method", new object[] { new CustomObject(), new CustomObject() }), binary: "lQGAwKZtZXRob2SShqpTdHJpbmdQcm9wqFNpZ25hbFIhqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqsRGF0ZVRpbWVQcm9w1v9Y7ByAqE51bGxQcm9wwKtCeXRlQXJyUHJvcMQDAQIDhqpTdHJpbmdQcm9wqFNpZ25hbFIhqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqsRGF0ZVRpbWVQcm9w1v9Y7ByAqE51bGxQcm9wwKtCeXRlQXJyUHJvcMQDAQID"), new ProtocolTestData( name: "InvocationWithHeadersNoIdAndArrayOfCustomObjectArgs", - message: AddHeaders(TestHeaders, new InvocationMessage("method", null, new object[] { new CustomObject(), new CustomObject() })), + message: AddHeaders(TestHeaders, new InvocationMessage("method", new object[] { new CustomObject(), new CustomObject() })), binary: "lQGDo0Zvb6NCYXKyS2V5V2l0aApOZXcNCkxpbmVzq1N0aWxsIFdvcmtzsVZhbHVlV2l0aE5ld0xpbmVzsEFsc28KV29ya3MNCkZpbmXApm1ldGhvZJKGqlN0cmluZ1Byb3CoU2lnbmFsUiGqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqxEYXRlVGltZVByb3DW/1jsHICoTnVsbFByb3DAq0J5dGVBcnJQcm9wxAMBAgOGqlN0cmluZ1Byb3CoU2lnbmFsUiGqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqxEYXRlVGltZVByb3DW/1jsHICoTnVsbFByb3DAq0J5dGVBcnJQcm9wxAMBAgM="), // StreamItem Messages @@ -187,35 +187,35 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol // StreamInvocation Messages new ProtocolTestData( name: "StreamInvocationWithNoHeadersAndNoArgs", - message: new StreamInvocationMessage("xyz", "method", null, Array.Empty()), + message: new StreamInvocationMessage("xyz", "method", Array.Empty()), binary: "lQSAo3h5eqZtZXRob2SQ"), new ProtocolTestData( name: "StreamInvocationWithNoHeadersAndNullArg", - message: new StreamInvocationMessage("xyz", "method", null, new object[] { null }), + message: new StreamInvocationMessage("xyz", "method", new object[] { null }), binary: "lQSAo3h5eqZtZXRob2SRwA=="), new ProtocolTestData( name: "StreamInvocationWithNoHeadersAndIntArg", - message: new StreamInvocationMessage("xyz", "method", null, new object[] { 42 }), + message: new StreamInvocationMessage("xyz", "method", new object[] { 42 }), binary: "lQSAo3h5eqZtZXRob2SRKg=="), new ProtocolTestData( name: "StreamInvocationWithNoHeadersAndEnumArg", - message: new StreamInvocationMessage("xyz", "method", null, new object[] { TestEnum.One }), + message: new StreamInvocationMessage("xyz", "method", new object[] { TestEnum.One }), binary: "lQSAo3h5eqZtZXRob2SRo09uZQ=="), new ProtocolTestData( name: "StreamInvocationWithNoHeadersAndIntAndStringArgs", - message: new StreamInvocationMessage("xyz", "method", null, new object[] { 42, "string" }), + message: new StreamInvocationMessage("xyz", "method", new object[] { 42, "string" }), binary: "lQSAo3h5eqZtZXRob2SSKqZzdHJpbmc="), new ProtocolTestData( name: "StreamInvocationWithNoHeadersAndIntStringAndCustomObjectArgs", - message: new StreamInvocationMessage("xyz", "method", null, new object[] { 42, "string", new CustomObject() }), + message: new StreamInvocationMessage("xyz", "method", new object[] { 42, "string", new CustomObject() }), binary: "lQSAo3h5eqZtZXRob2STKqZzdHJpbmeGqlN0cmluZ1Byb3CoU2lnbmFsUiGqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqxEYXRlVGltZVByb3DW/1jsHICoTnVsbFByb3DAq0J5dGVBcnJQcm9wxAMBAgM="), new ProtocolTestData( name: "StreamInvocationWithNoHeadersAndCustomObjectArrayArg", - message: new StreamInvocationMessage("xyz", "method", null, new object[] { new CustomObject(), new CustomObject() }), + message: new StreamInvocationMessage("xyz", "method", new object[] { new CustomObject(), new CustomObject() }), binary: "lQSAo3h5eqZtZXRob2SShqpTdHJpbmdQcm9wqFNpZ25hbFIhqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqsRGF0ZVRpbWVQcm9w1v9Y7ByAqE51bGxQcm9wwKtCeXRlQXJyUHJvcMQDAQIDhqpTdHJpbmdQcm9wqFNpZ25hbFIhqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqsRGF0ZVRpbWVQcm9w1v9Y7ByAqE51bGxQcm9wwKtCeXRlQXJyUHJvcMQDAQID"), new ProtocolTestData( name: "StreamInvocationWithHeadersAndCustomObjectArrayArg", - message: AddHeaders(TestHeaders, new StreamInvocationMessage("xyz", "method", null, new object[] { new CustomObject(), new CustomObject() })), + message: AddHeaders(TestHeaders, new StreamInvocationMessage("xyz", "method", new object[] { new CustomObject(), new CustomObject() })), binary: "lQSDo0Zvb6NCYXKyS2V5V2l0aApOZXcNCkxpbmVzq1N0aWxsIFdvcmtzsVZhbHVlV2l0aE5ld0xpbmVzsEFsc28KV29ya3MNCkZpbmWjeHl6pm1ldGhvZJKGqlN0cmluZ1Byb3CoU2lnbmFsUiGqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqxEYXRlVGltZVByb3DW/1jsHICoTnVsbFByb3DAq0J5dGVBcnJQcm9wxAMBAgOGqlN0cmluZ1Byb3CoU2lnbmFsUiGqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqxEYXRlVGltZVByb3DW/1jsHICoTnVsbFByb3DAq0J5dGVBcnJQcm9wxAMBAgM="), // CancelInvocation Messages @@ -256,7 +256,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol [Fact] public void ParseMessageWithExtraData() { - var expectedMessage = new InvocationMessage("xyz", "method", null, Array.Empty()); + var expectedMessage = new InvocationMessage("xyz", "method", Array.Empty()); // Verify that the input binary string decodes to the expected MsgPack primitives var bytes = new byte[] { ArrayBytes(6), 1, 0x80, StringBytes(3), (byte)'x', (byte)'y', (byte)'z', StringBytes(6), (byte)'m', (byte)'e', (byte)'t', (byte)'h', (byte)'o', (byte)'d', ArrayBytes(0), StringBytes(2), (byte)'e', (byte)'x' }; @@ -422,9 +422,8 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol var binder = new TestBinder(new[] { typeof(string) }, typeof(string)); var data = new ReadOnlySequence(buffer); _hubProtocol.TryParseMessage(ref data, binder, out var message); - var exception = Assert.Throws(() => ((HubMethodInvocationMessage)message).Arguments); - - Assert.Equal(testData.ErrorMessage, exception.Message); + var bindingFailure = Assert.IsType(message); + Assert.Equal(testData.ErrorMessage, bindingFailure.BindingFailure.SourceException.Message); } [Theory] diff --git a/test/Microsoft.AspNetCore.SignalR.Redis.Tests/RedisProtocolTests.cs b/test/Microsoft.AspNetCore.SignalR.Redis.Tests/RedisProtocolTests.cs index 7d1a4aa3e4..3a710f42cc 100644 --- a/test/Microsoft.AspNetCore.SignalR.Redis.Tests/RedisProtocolTests.cs +++ b/test/Microsoft.AspNetCore.SignalR.Redis.Tests/RedisProtocolTests.cs @@ -84,12 +84,14 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests } // The actual invocation message doesn't matter - private static InvocationMessage _testMessage = new InvocationMessage("target", null, Array.Empty()); - private static Dictionary> _invocationTestData = new[] + private static InvocationMessage _testMessage = new InvocationMessage("target", Array.Empty()); + + // We use a func so we are guaranteed to get a new SerializedHubMessage for each test + private static Dictionary>> _invocationTestData = new[] { - CreateTestData( + CreateTestData>( "NoExcludedIds", - new RedisInvocation(new SerializedHubMessage(_testMessage), null), + () => new RedisInvocation(new SerializedHubMessage(_testMessage), null), 0x92, 0x90, 0x82, @@ -97,9 +99,9 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests 0xC4, 0x01, 0x2A, 0xA2, (byte)'p', (byte)'2', 0xC4, 0x01, 0x2A), - CreateTestData( + CreateTestData>( "OneExcludedId", - new RedisInvocation(new SerializedHubMessage(_testMessage), new [] { "a" }), + () => new RedisInvocation(new SerializedHubMessage(_testMessage), new [] { "a" }), 0x92, 0x91, 0xA1, (byte)'a', @@ -108,9 +110,9 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests 0xC4, 0x01, 0x2A, 0xA2, (byte)'p', (byte)'2', 0xC4, 0x01, 0x2A), - CreateTestData( + CreateTestData>( "ManyExcludedIds", - new RedisInvocation(new SerializedHubMessage(_testMessage), new [] { "a", "b", "c", "d", "e", "f" }), + () => new RedisInvocation(new SerializedHubMessage(_testMessage), new [] { "a", "b", "c", "d", "e", "f" }), 0x92, 0x96, 0xA1, (byte)'a', @@ -136,15 +138,17 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests var hubProtocols = new[] { new DummyHubProtocol("p1"), new DummyHubProtocol("p2") }; var protocol = new RedisProtocol(hubProtocols); + var expected = testData.Decoded(); + var decoded = protocol.ReadInvocation(testData.Encoded); - Assert.Equal(testData.Decoded.ExcludedConnectionIds, decoded.ExcludedConnectionIds); + Assert.Equal(expected.ExcludedConnectionIds, decoded.ExcludedConnectionIds); // Verify the deserialized object has the necessary serialized forms foreach (var hubProtocol in hubProtocols) { Assert.Equal( - testData.Decoded.Message.GetSerializedMessage(hubProtocol).ToArray(), + expected.Message.GetSerializedMessage(hubProtocol).ToArray(), decoded.Message.GetSerializedMessage(hubProtocol).ToArray()); Assert.Equal(1, hubProtocol.SerializationCount); } @@ -159,7 +163,8 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests // Actual invocation doesn't matter because we're using a dummy hub protocol. // But the dummy protocol will check that we gave it the test message to make sure everything flows through properly. - var encoded = protocol.WriteInvocation(_testMessage.Target, _testMessage.Arguments, testData.Decoded.ExcludedConnectionIds); + var expected = testData.Decoded(); + var encoded = protocol.WriteInvocation(_testMessage.Target, _testMessage.Arguments, expected.ExcludedConnectionIds); Assert.Equal(testData.Encoded, encoded); } diff --git a/test/Microsoft.AspNetCore.SignalR.Tests.Utils/TestClient.cs b/test/Microsoft.AspNetCore.SignalR.Tests.Utils/TestClient.cs index 80f78c03bc..9ecf005342 100644 --- a/test/Microsoft.AspNetCore.SignalR.Tests.Utils/TestClient.cs +++ b/test/Microsoft.AspNetCore.SignalR.Tests.Utils/TestClient.cs @@ -164,13 +164,13 @@ namespace Microsoft.AspNetCore.SignalR.Tests public Task SendInvocationAsync(string methodName, bool nonBlocking, params object[] args) { var invocationId = nonBlocking ? null : GetInvocationId(); - return SendHubMessageAsync(new InvocationMessage(invocationId, methodName, null, args)); + return SendHubMessageAsync(new InvocationMessage(invocationId, methodName, args)); } public Task SendStreamInvocationAsync(string methodName, params object[] args) { var invocationId = GetInvocationId(); - return SendHubMessageAsync(new StreamInvocationMessage(invocationId, methodName, null, args)); + return SendHubMessageAsync(new StreamInvocationMessage(invocationId, methodName, args)); } public async Task SendHubMessageAsync(HubMessage message) diff --git a/test/Microsoft.AspNetCore.SignalR.Tests/HubConnectionHandlerTests.cs b/test/Microsoft.AspNetCore.SignalR.Tests/HubConnectionHandlerTests.cs index 9076587e92..3ea5d2e4f7 100644 --- a/test/Microsoft.AspNetCore.SignalR.Tests/HubConnectionHandlerTests.cs +++ b/test/Microsoft.AspNetCore.SignalR.Tests/HubConnectionHandlerTests.cs @@ -1429,7 +1429,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests await client.Connected.OrTimeout(); var invocationId = Guid.NewGuid().ToString("N"); - await client.SendHubMessageAsync(new StreamInvocationMessage(invocationId, nameof(StreamingHub.BlockingStream), null, Array.Empty())); + await client.SendHubMessageAsync(new StreamInvocationMessage(invocationId, nameof(StreamingHub.BlockingStream), Array.Empty())); // cancel the Streaming method await client.SendHubMessageAsync(new CancelInvocationMessage(invocationId)).OrTimeout(); diff --git a/test/Microsoft.AspNetCore.SignalR.Tests/HubMethodInvocationMessageTests.cs b/test/Microsoft.AspNetCore.SignalR.Tests/HubMethodInvocationMessageTests.cs deleted file mode 100644 index dedf151d0a..0000000000 --- a/test/Microsoft.AspNetCore.SignalR.Tests/HubMethodInvocationMessageTests.cs +++ /dev/null @@ -1,29 +0,0 @@ -// 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. - -using System; -using System.Runtime.ExceptionServices; -using Microsoft.AspNetCore.SignalR.Protocol; -using Xunit; - -namespace Microsoft.AspNetCore.SignalR.Tests -{ - public class HubMethodInvocationMessageTests - { - [Fact] - public void InvocationMessageToStringPutsErrorInArgs() - { - var hubMessage = new InvocationMessage("echo", ExceptionDispatchInfo.Capture(new Exception("test")), null); - - Assert.Equal("InvocationMessage { InvocationId: \"\", Target: \"echo\", Arguments: [ Error: test ] }", hubMessage.ToString()); - } - - [Fact] - public void StreamInvocationMessageToStringPutsErrorInArgs() - { - var hubMessage = new StreamInvocationMessage("3", "echo", ExceptionDispatchInfo.Capture(new Exception("test")), null); - - Assert.Equal("StreamInvocation { InvocationId: \"3\", Target: \"echo\", Arguments: [ Error: test ] }", hubMessage.ToString()); - } - } -}