Late parameter binding (#1049)
Late parameter binding Storing exception thrown during parameter binding and rethrowing when the method is about to throw. This allows completing invocations with a HubException and keeping the connection open. We will also no longer close the connection if parameters for client side methods cannot be bound. We will log and continue. Fixes: #818 (Also fixing #1005 because I was just touching this line)
This commit is contained in:
parent
eec6b4f2f5
commit
18f770e937
|
|
@ -19,7 +19,6 @@ using Microsoft.AspNetCore.Sockets.Features;
|
|||
using Microsoft.AspNetCore.Sockets.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.AspNetCore.SignalR.Client
|
||||
{
|
||||
|
|
@ -122,7 +121,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
|||
public IDisposable On(string methodName, Type[] parameterTypes, Func<object[], object, Task> handler, object state)
|
||||
{
|
||||
var invocationHandler = new InvocationHandler(parameterTypes, handler, state);
|
||||
var invocationList = _handlers.AddOrUpdate(methodName, _ => new List<InvocationHandler> { invocationHandler },
|
||||
var invocationList = _handlers.AddOrUpdate(methodName, _ => new List<InvocationHandler> { invocationHandler },
|
||||
(_, invocations) =>
|
||||
{
|
||||
lock (invocations)
|
||||
|
|
@ -135,7 +134,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
|||
return new Subscription(invocationHandler, invocationList);
|
||||
}
|
||||
|
||||
public async Task<ReadableChannel<object>> StreamAsync(string methodName, Type returnType, object[] args, CancellationToken cancellationToken = default(CancellationToken))
|
||||
public async Task<ReadableChannel<object>> StreamAsync(string methodName, Type returnType, object[] args, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await StreamAsyncCore(methodName, returnType, args, cancellationToken).ForceAsync();
|
||||
}
|
||||
|
|
@ -174,7 +173,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
|||
return channel;
|
||||
}
|
||||
|
||||
public async Task<object> InvokeAsync(string methodName, Type returnType, object[] args, CancellationToken cancellationToken = default(CancellationToken)) =>
|
||||
public async Task<object> InvokeAsync(string methodName, Type returnType, object[] args, CancellationToken cancellationToken = default) =>
|
||||
await InvokeAsyncCore(methodName, returnType, args, cancellationToken).ForceAsync();
|
||||
|
||||
private async Task<object> InvokeAsyncCore(string methodName, Type returnType, object[] args, CancellationToken cancellationToken)
|
||||
|
|
@ -184,7 +183,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
|||
return await task;
|
||||
}
|
||||
|
||||
public async Task SendAsync(string methodName, object[] args, CancellationToken cancellationToken = default(CancellationToken)) =>
|
||||
public async Task SendAsync(string methodName, object[] args, CancellationToken cancellationToken = default) =>
|
||||
await SendAsyncCore(methodName, args, cancellationToken).ForceAsync();
|
||||
|
||||
private Task SendAsyncCore(string methodName, object[] args, CancellationToken cancellationToken)
|
||||
|
|
@ -206,7 +205,8 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
|||
}
|
||||
|
||||
// Create an invocation descriptor. Client invocations are always blocking
|
||||
var invocationMessage = new InvocationMessage(irq.InvocationId, nonBlocking, methodName, args);
|
||||
var invocationMessage = new InvocationMessage(irq.InvocationId, nonBlocking, methodName,
|
||||
argumentBindingException: null, arguments: args);
|
||||
|
||||
// We don't need to track invocations for fire an forget calls
|
||||
if (!nonBlocking)
|
||||
|
|
@ -252,7 +252,8 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
|||
switch (message)
|
||||
{
|
||||
case InvocationMessage invocation:
|
||||
_logger.ReceivedInvocation(invocation.InvocationId, invocation.Target, invocation.Arguments);
|
||||
_logger.ReceivedInvocation(invocation.InvocationId, invocation.Target,
|
||||
invocation.ArgumentBindingException != null ? null : invocation.Arguments);
|
||||
await DispatchInvocationAsync(invocation, _connectionActive.Token);
|
||||
break;
|
||||
case CompletionMessage completion:
|
||||
|
|
@ -344,7 +345,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ExceptionThrownFromCallback(nameof(On), ex);
|
||||
_logger.ErrorInvokingClientSideMethod(invocation.Target, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,8 +112,8 @@ namespace Microsoft.AspNetCore.SignalR.Client.Internal
|
|||
private static readonly Action<ILogger, string, Exception> _streamItemOnNonStreamInvocation =
|
||||
LoggerMessage.Define<string>(LogLevel.Error, new EventId(4, nameof(StreamItemOnNonStreamInvocation)), "Invocation {invocationId} received stream item but was invoked as a non-streamed invocation.");
|
||||
|
||||
private static readonly Action<ILogger, string, Exception> _exceptionThrownFromCallback =
|
||||
LoggerMessage.Define<string>(LogLevel.Error, new EventId(5, nameof(ExceptionThrownFromCallback)), "An exception was thrown from the '{callback}' callback");
|
||||
private static readonly Action<ILogger, string, Exception> _errorInvokingClientSideMethod =
|
||||
LoggerMessage.Define<string>(LogLevel.Error, new EventId(5, nameof(ErrorInvokingClientSideMethod)), "Invoking client side method '{methodName}' failed.");
|
||||
|
||||
private static readonly Action<ILogger, string, string, Exception> _receivedUnexpectedMessageTypeForInvokeCompletion =
|
||||
LoggerMessage.Define<string, string>(LogLevel.Error, new EventId(6, nameof(ReceivedUnexpectedMessageTypeForInvokeCompletion)), "Invocation {invocationId} was invoked as a non-streaming hub method but completed with '{messageType}' message.");
|
||||
|
|
@ -137,7 +137,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Internal
|
|||
{
|
||||
if (logger.IsEnabled(LogLevel.Trace))
|
||||
{
|
||||
var argsList = string.Join(", ", args.Select(a => a.GetType().FullName));
|
||||
var argsList = args == null ? string.Empty : string.Join(", ", args.Select(a => a?.GetType().FullName ?? "(null)"));
|
||||
_issueInvocation(logger, invocationId, returnType, methodName, argsList, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -161,7 +161,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Internal
|
|||
{
|
||||
if (logger.IsEnabled(LogLevel.Trace))
|
||||
{
|
||||
var argsList = string.Join(", ", args.Select(a => a.GetType().FullName));
|
||||
var argsList = args == null ? string.Empty : string.Join(", ", args.Select(a => a?.GetType().FullName ?? "(null)"));
|
||||
_receivedInvocation(logger, invocationId, methodName, argsList, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -286,9 +286,9 @@ namespace Microsoft.AspNetCore.SignalR.Client.Internal
|
|||
_streamItemOnNonStreamInvocation(logger, invocationId, null);
|
||||
}
|
||||
|
||||
public static void ExceptionThrownFromCallback(this ILogger logger, string callbackName, Exception exception)
|
||||
public static void ErrorInvokingClientSideMethod(this ILogger logger, string methodName, Exception exception)
|
||||
{
|
||||
_exceptionThrownFromCallback(logger, callbackName, exception);
|
||||
_errorInvokingClientSideMethod(logger, methodName, exception);
|
||||
}
|
||||
|
||||
public static void ReceivedUnexpectedMessageTypeForStreamCompletion(this ILogger logger, string invocationId, string messageType)
|
||||
|
|
|
|||
|
|
@ -3,18 +3,42 @@
|
|||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.ExceptionServices;
|
||||
|
||||
namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
|
||||
{
|
||||
public class InvocationMessage : HubMessage
|
||||
{
|
||||
private readonly ExceptionDispatchInfo _argumentBindingException;
|
||||
private readonly object[] _arguments;
|
||||
|
||||
public string Target { get; }
|
||||
|
||||
public object[] Arguments { get; }
|
||||
public object[] Arguments
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_argumentBindingException != null)
|
||||
{
|
||||
_argumentBindingException.Throw();
|
||||
}
|
||||
|
||||
return _arguments;
|
||||
}
|
||||
}
|
||||
|
||||
public Exception ArgumentBindingException
|
||||
{
|
||||
get
|
||||
{
|
||||
return _argumentBindingException?.SourceException;
|
||||
}
|
||||
}
|
||||
|
||||
public bool NonBlocking { get; }
|
||||
|
||||
public InvocationMessage(string invocationId, bool nonBlocking, string target, params object[] arguments) : base(invocationId)
|
||||
public InvocationMessage(string invocationId, bool nonBlocking, string target, ExceptionDispatchInfo argumentBindingException, params object[] arguments)
|
||||
: base(invocationId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(invocationId))
|
||||
{
|
||||
|
|
@ -26,13 +50,14 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
|
|||
throw new ArgumentNullException(nameof(target));
|
||||
}
|
||||
|
||||
if (arguments == null)
|
||||
if ((arguments == null && argumentBindingException == null) || (arguments?.Length > 0 && argumentBindingException != null))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(arguments));
|
||||
throw new ArgumentException($"'{nameof(argumentBindingException)}' and '{nameof(arguments)}' are mutually exclusive");
|
||||
}
|
||||
|
||||
Target = target;
|
||||
Arguments = arguments;
|
||||
_arguments = arguments;
|
||||
_argumentBindingException = argumentBindingException;
|
||||
NonBlocking = nonBlocking;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
|
@ -242,22 +242,40 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
|
|||
var args = JsonUtils.GetRequiredProperty<JArray>(json, ArgumentsPropertyName, JTokenType.Array);
|
||||
|
||||
var paramTypes = binder.GetParameterTypes(target);
|
||||
|
||||
try
|
||||
{
|
||||
var arguments = BindArguments(args, paramTypes);
|
||||
return new InvocationMessage(invocationId, nonBlocking, target, argumentBindingException: null, arguments: arguments);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new InvocationMessage(invocationId, nonBlocking, target, ExceptionDispatchInfo.Capture(ex));
|
||||
}
|
||||
}
|
||||
|
||||
private object[] BindArguments(JArray args, Type[] paramTypes)
|
||||
{
|
||||
var arguments = new object[args.Count];
|
||||
if (paramTypes.Length != arguments.Length)
|
||||
{
|
||||
throw new FormatException($"Invocation provides {arguments.Length} argument(s) but target expects {paramTypes.Length}.");
|
||||
}
|
||||
|
||||
for (var i = 0; i < paramTypes.Length; i++)
|
||||
try
|
||||
{
|
||||
var paramType = paramTypes[i];
|
||||
for (var i = 0; i < paramTypes.Length; i++)
|
||||
{
|
||||
var paramType = paramTypes[i];
|
||||
arguments[i] = args[i].ToObject(paramType, _payloadSerializer);
|
||||
}
|
||||
|
||||
// TODO(anurse): We can add some DI magic here to allow users to provide their own serialization
|
||||
// Related Bug: https://github.com/aspnet/SignalR/issues/261
|
||||
arguments[i] = args[i].ToObject(paramType, _payloadSerializer);
|
||||
return arguments;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new FormatException("Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.", ex);
|
||||
}
|
||||
|
||||
return new InvocationMessage(invocationId, nonBlocking, target, arguments);
|
||||
}
|
||||
|
||||
private StreamItemMessage BindResultMessage(JObject json, IInvocationBinder binder)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||
using MsgPack;
|
||||
using MsgPack.Serialization;
|
||||
|
|
@ -55,24 +56,26 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
|
|||
|
||||
private static HubMessage ParseMessage(Stream input, IInvocationBinder binder)
|
||||
{
|
||||
var unpacker = Unpacker.Create(input);
|
||||
var arraySize = ReadArrayLength(unpacker, "elementCount");
|
||||
var messageType = ReadInt32(unpacker, "messageType");
|
||||
|
||||
switch (messageType)
|
||||
using (var unpacker = Unpacker.Create(input))
|
||||
{
|
||||
case InvocationMessageType:
|
||||
return CreateInvocationMessage(unpacker, binder);
|
||||
case StreamItemMessageType:
|
||||
return CreateStreamItemMessage(unpacker, binder);
|
||||
case CompletionMessageType:
|
||||
return CreateCompletionMessage(unpacker, binder);
|
||||
case StreamCompletionMessageType:
|
||||
return CreateStreamCompletionMessage(unpacker, arraySize, binder);
|
||||
case CancelInvocationMessageType:
|
||||
return CreateCancelInvocationMessage(unpacker);
|
||||
default:
|
||||
throw new FormatException($"Invalid message type: {messageType}.");
|
||||
var arraySize = ReadArrayLength(unpacker, "elementCount");
|
||||
var messageType = ReadInt32(unpacker, "messageType");
|
||||
|
||||
switch (messageType)
|
||||
{
|
||||
case InvocationMessageType:
|
||||
return CreateInvocationMessage(unpacker, binder);
|
||||
case StreamItemMessageType:
|
||||
return CreateStreamItemMessage(unpacker, binder);
|
||||
case CompletionMessageType:
|
||||
return CreateCompletionMessage(unpacker, binder);
|
||||
case StreamCompletionMessageType:
|
||||
return CreateStreamCompletionMessage(unpacker, arraySize, binder);
|
||||
case CancelInvocationMessageType:
|
||||
return CreateCancelInvocationMessage(unpacker);
|
||||
default:
|
||||
throw new FormatException($"Invalid message type: {messageType}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -81,22 +84,43 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
|
|||
var invocationId = ReadInvocationId(unpacker);
|
||||
var nonBlocking = ReadBoolean(unpacker, "nonBlocking");
|
||||
var target = ReadString(unpacker, "target");
|
||||
var argumentCount = ReadArrayLength(unpacker, "arguments");
|
||||
var parameterTypes = binder.GetParameterTypes(target);
|
||||
|
||||
try
|
||||
{
|
||||
var arguments = BindArguments(unpacker, parameterTypes);
|
||||
return new InvocationMessage(invocationId, nonBlocking, target, argumentBindingException: null, arguments: arguments);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new InvocationMessage(invocationId, nonBlocking, target, ExceptionDispatchInfo.Capture(ex));
|
||||
}
|
||||
}
|
||||
|
||||
private static object[] BindArguments(Unpacker unpacker, Type[] parameterTypes)
|
||||
{
|
||||
var argumentCount = ReadArrayLength(unpacker, "arguments");
|
||||
|
||||
if (parameterTypes.Length != argumentCount)
|
||||
{
|
||||
throw new FormatException(
|
||||
$"Target method expects {parameterTypes.Length} arguments(s) but invocation has {argumentCount} argument(s).");
|
||||
$"Invocation provides {argumentCount} argument(s) but target expects {parameterTypes.Length}.");
|
||||
}
|
||||
|
||||
var arguments = new object[argumentCount];
|
||||
for (var i = 0; i < argumentCount; i++)
|
||||
try
|
||||
{
|
||||
arguments[i] = DeserializeObject(unpacker, parameterTypes[i], "argument");
|
||||
}
|
||||
var arguments = new object[argumentCount];
|
||||
for (var i = 0; i < argumentCount; i++)
|
||||
{
|
||||
arguments[i] = DeserializeObject(unpacker, parameterTypes[i], "argument");
|
||||
}
|
||||
|
||||
return new InvocationMessage(invocationId, nonBlocking, target, arguments);
|
||||
return arguments;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new FormatException("Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static StreamItemMessage CreateStreamItemMessage(Unpacker unpacker, IInvocationBinder binder)
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ namespace Microsoft.AspNetCore.SignalR
|
|||
|
||||
private InvocationMessage CreateInvocationMessage(string methodName, object[] args)
|
||||
{
|
||||
return new InvocationMessage(GetInvocationId(), nonBlocking: true, target: methodName, arguments: args);
|
||||
return new InvocationMessage(GetInvocationId(), nonBlocking: true, target: methodName, argumentBindingException: null, arguments: args);
|
||||
}
|
||||
|
||||
public override Task InvokeUserAsync(string userId, string methodName, object[] args)
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis
|
|||
|
||||
public override Task InvokeAllAsync(string methodName, object[] args)
|
||||
{
|
||||
var message = new InvocationMessage(GetInvocationId(), nonBlocking: true, target: methodName, arguments: args);
|
||||
var message = new InvocationMessage(GetInvocationId(), nonBlocking: true, target: methodName, argumentBindingException: null, arguments: args);
|
||||
|
||||
return PublishAsync(_channelNamePrefix, message);
|
||||
}
|
||||
|
|
@ -203,7 +203,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis
|
|||
throw new ArgumentNullException(nameof(connectionId));
|
||||
}
|
||||
|
||||
var message = new InvocationMessage(GetInvocationId(), nonBlocking: true, target: methodName, arguments: args);
|
||||
var message = new InvocationMessage(GetInvocationId(), nonBlocking: true, target: methodName, argumentBindingException: null, arguments: args);
|
||||
|
||||
// If the connection is local we can skip sending the message through the bus since we require sticky connections.
|
||||
// This also saves serializing and deserializing the message!
|
||||
|
|
@ -223,14 +223,14 @@ namespace Microsoft.AspNetCore.SignalR.Redis
|
|||
throw new ArgumentNullException(nameof(groupName));
|
||||
}
|
||||
|
||||
var message = new InvocationMessage(GetInvocationId(), nonBlocking: true, target: methodName, arguments: args);
|
||||
var message = new InvocationMessage(GetInvocationId(), nonBlocking: true, target: methodName, argumentBindingException: null, arguments: args);
|
||||
|
||||
return PublishAsync(_channelNamePrefix + ".group." + groupName, message);
|
||||
}
|
||||
|
||||
public override Task InvokeUserAsync(string userId, string methodName, object[] args)
|
||||
{
|
||||
var message = new InvocationMessage(GetInvocationId(), nonBlocking: true, target: methodName, arguments: args);
|
||||
var message = new InvocationMessage(GetInvocationId(), nonBlocking: true, target: methodName, argumentBindingException: null, arguments: args);
|
||||
|
||||
return PublishAsync(_channelNamePrefix + ".user." + userId, message);
|
||||
}
|
||||
|
|
@ -556,7 +556,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis
|
|||
public IReadOnlyList<string> ExcludedIds;
|
||||
|
||||
public RedisExcludeClientsMessage(string invocationId, bool nonBlocking, string target, IReadOnlyList<string> excludedIds, params object[] arguments)
|
||||
: base(invocationId, nonBlocking, target, arguments)
|
||||
: base(invocationId, nonBlocking, target, argumentBindingException: null, arguments: arguments)
|
||||
{
|
||||
ExcludedIds = excludedIds;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -391,7 +391,7 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
|||
_logger.EndReceive(_connectionId);
|
||||
}
|
||||
|
||||
public async Task SendAsync(byte[] data, CancellationToken cancellationToken = default(CancellationToken)) =>
|
||||
public async Task SendAsync(byte[] data, CancellationToken cancellationToken = default) =>
|
||||
await SendAsyncCore(data, cancellationToken).ForceAsync();
|
||||
|
||||
private async Task SendAsyncCore(byte[] data, CancellationToken cancellationToken)
|
||||
|
|
|
|||
|
|
@ -136,7 +136,8 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
|||
public Task<string> SendInvocationAsync(string methodName, bool nonBlocking, params object[] args)
|
||||
{
|
||||
var invocationId = GetInvocationId();
|
||||
return SendHubMessageAsync(new InvocationMessage(invocationId, nonBlocking, methodName, args));
|
||||
return SendHubMessageAsync(new InvocationMessage(invocationId, nonBlocking, methodName,
|
||||
argumentBindingException: null, arguments: args));
|
||||
}
|
||||
|
||||
public async Task<string> SendHubMessageAsync(HubMessage message)
|
||||
|
|
|
|||
|
|
@ -323,7 +323,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
|
|||
|
||||
[Theory]
|
||||
[MemberData(nameof(HubProtocolsAndTransportsAndHubPaths))]
|
||||
public async Task ServerClosesConnectionIfHubMethodCannotBeResolved(IHubProtocol hubProtocol, TransportType transportType, string hubPath)
|
||||
public async Task ServerThrowsHubExceptionIfHubMethodCannotBeResolved(IHubProtocol hubProtocol, TransportType transportType, string hubPath)
|
||||
{
|
||||
using (StartLog(out var loggerFactory))
|
||||
{
|
||||
|
|
@ -333,9 +333,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
|
|||
{
|
||||
await connection.StartAsync().OrTimeout();
|
||||
|
||||
var ex = await Assert.ThrowsAnyAsync<Exception>(
|
||||
async () => await connection.InvokeAsync("!@#$%")).OrTimeout();
|
||||
|
||||
var ex = await Assert.ThrowsAsync<HubException>(() => connection.InvokeAsync("!@#$%")).OrTimeout();
|
||||
Assert.Equal("Unknown hub method '!@#$%'", ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -350,6 +348,145 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
|
|||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(HubProtocolsAndTransportsAndHubPaths))]
|
||||
public async Task ServerThrowsHubExceptionOnHubMethodArgumentCountMismatch(IHubProtocol hubProtocol, TransportType transportType, string hubPath)
|
||||
{
|
||||
using (StartLog(out var loggerFactory))
|
||||
{
|
||||
var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + hubPath), transportType, loggerFactory);
|
||||
var connection = new HubConnection(httpConnection, hubProtocol, loggerFactory);
|
||||
try
|
||||
{
|
||||
await connection.StartAsync().OrTimeout();
|
||||
|
||||
var ex = await Assert.ThrowsAsync<HubException>(() => connection.InvokeAsync("Echo", "p1", 42)).OrTimeout();
|
||||
Assert.Equal("Invocation provides 2 argument(s) but target expects 1.", ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
loggerFactory.CreateLogger<HubConnectionTests>().LogError(ex, "Exception from test");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
await connection.DisposeAsync().OrTimeout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(HubProtocolsAndTransportsAndHubPaths))]
|
||||
public async Task ServerThrowsHubExceptionOnHubMethodArgumentTypeMismatch(IHubProtocol hubProtocol, TransportType transportType, string hubPath)
|
||||
{
|
||||
using (StartLog(out var loggerFactory))
|
||||
{
|
||||
var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + hubPath), transportType, loggerFactory);
|
||||
var connection = new HubConnection(httpConnection, hubProtocol, loggerFactory);
|
||||
try
|
||||
{
|
||||
await connection.StartAsync().OrTimeout();
|
||||
|
||||
var ex = await Assert.ThrowsAsync<HubException>(() => connection.InvokeAsync("Echo", new int[] { 42 })).OrTimeout();
|
||||
Assert.StartsWith("Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.", ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
loggerFactory.CreateLogger<HubConnectionTests>().LogError(ex, "Exception from test");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
await connection.DisposeAsync().OrTimeout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory(Skip="https://github.com/aspnet/SignalR/issues/1053")]
|
||||
[MemberData(nameof(HubProtocolsAndTransportsAndHubPaths))]
|
||||
public async Task ServerThrowsHubExceptionIfStreamingHubMethodCannotBeResolved(IHubProtocol hubProtocol, TransportType transportType, string hubPath)
|
||||
{
|
||||
using (StartLog(out var loggerFactory))
|
||||
{
|
||||
var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + hubPath), transportType, loggerFactory);
|
||||
var connection = new HubConnection(httpConnection, hubProtocol, loggerFactory);
|
||||
try
|
||||
{
|
||||
await connection.StartAsync().OrTimeout();
|
||||
|
||||
var channel = await connection.StreamAsync<int>("!@#$%");
|
||||
var ex = await Assert.ThrowsAsync<HubException>(() => channel.ReadAllAsync().OrTimeout());
|
||||
Assert.Equal("Unknown hub method '!@#$%'", ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
loggerFactory.CreateLogger<HubConnectionTests>().LogError(ex, "Exception from test");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
await connection.DisposeAsync().OrTimeout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(HubProtocolsAndTransportsAndHubPaths))]
|
||||
public async Task ServerThrowsHubExceptionOnStreamingHubMethodArgumentCountMismatch(IHubProtocol hubProtocol, TransportType transportType, string hubPath)
|
||||
{
|
||||
using (StartLog(out var loggerFactory))
|
||||
{
|
||||
loggerFactory.AddConsole(LogLevel.Trace);
|
||||
var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + hubPath), transportType, loggerFactory);
|
||||
var connection = new HubConnection(httpConnection, hubProtocol, loggerFactory);
|
||||
try
|
||||
{
|
||||
await connection.StartAsync().OrTimeout();
|
||||
|
||||
var channel = await connection.StreamAsync<int>("Stream", 42, 42);
|
||||
var ex = await Assert.ThrowsAsync<HubException>(() => channel.ReadAllAsync().OrTimeout());
|
||||
Assert.Equal("Invocation provides 2 argument(s) but target expects 1.", ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
loggerFactory.CreateLogger<HubConnectionTests>().LogError(ex, "Exception from test");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
await connection.DisposeAsync().OrTimeout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(HubProtocolsAndTransportsAndHubPaths))]
|
||||
public async Task ServerThrowsHubExceptionOnStreamingHubMethodArgumentTypeMismatch(IHubProtocol hubProtocol, TransportType transportType, string hubPath)
|
||||
{
|
||||
using (StartLog(out var loggerFactory))
|
||||
{
|
||||
var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + hubPath), transportType, loggerFactory);
|
||||
var connection = new HubConnection(httpConnection, hubProtocol, loggerFactory);
|
||||
try
|
||||
{
|
||||
await connection.StartAsync().OrTimeout();
|
||||
|
||||
var channel = await connection.StreamAsync<int>("Stream", "xyz");
|
||||
var ex = await Assert.ThrowsAsync<HubException>(() => channel.ReadAllAsync().OrTimeout());
|
||||
Assert.StartsWith("Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.", ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
loggerFactory.CreateLogger<HubConnectionTests>().LogError(ex, "Exception from test");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
await connection.DisposeAsync().OrTimeout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> HubProtocolsAndTransportsAndHubPaths
|
||||
{
|
||||
get
|
||||
|
|
|
|||
|
|
@ -123,28 +123,16 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ConnectionClosedOnCallbackArgumentCountMismatch()
|
||||
public async Task ConnectionNotClosedOnCallbackArgumentCountMismatch()
|
||||
{
|
||||
var connection = new TestConnection();
|
||||
var hubConnection = new HubConnection(connection, new JsonHubProtocol(new JsonSerializer()), new LoggerFactory());
|
||||
var closeTcs = new TaskCompletionSource<Exception>();
|
||||
hubConnection.Closed += e =>
|
||||
{
|
||||
if (e == null)
|
||||
{
|
||||
closeTcs.TrySetResult(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
closeTcs.TrySetException(e);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
var receiveTcs = new TaskCompletionSource<int>();
|
||||
|
||||
try
|
||||
{
|
||||
hubConnection.On<int>("Foo", r => { });
|
||||
await hubConnection.StartAsync();
|
||||
hubConnection.On<int>("Foo", r => { receiveTcs.SetResult(r); });
|
||||
await hubConnection.StartAsync().OrTimeout();
|
||||
|
||||
await connection.ReceiveJsonMessage(
|
||||
new
|
||||
|
|
@ -155,39 +143,34 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
|||
arguments = new object[] { 42, "42" }
|
||||
}).OrTimeout();
|
||||
|
||||
var ex = await Assert.ThrowsAsync<FormatException>(async () => await closeTcs.Task.OrTimeout());
|
||||
Assert.Equal("Invocation provides 2 argument(s) but target expects 1.", ex.Message);
|
||||
await connection.ReceiveJsonMessage(
|
||||
new
|
||||
{
|
||||
invocationId = "2",
|
||||
type = 1,
|
||||
target = "Foo",
|
||||
arguments = new object[] { 42 }
|
||||
}).OrTimeout();
|
||||
|
||||
Assert.Equal(42, await receiveTcs.Task.OrTimeout());
|
||||
}
|
||||
finally
|
||||
{
|
||||
await hubConnection.DisposeAsync().OrTimeout();
|
||||
await connection.DisposeAsync().OrTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ConnectionClosedOnCallbackArgumentTypeMismatch()
|
||||
public async Task ConnectionNotClosedOnCallbackArgumentTypeMismatch()
|
||||
{
|
||||
var connection = new TestConnection();
|
||||
var hubConnection = new HubConnection(connection, new JsonHubProtocol(), new LoggerFactory());
|
||||
var closeTcs = new TaskCompletionSource<Exception>();
|
||||
hubConnection.Closed += e =>
|
||||
{
|
||||
if (e == null)
|
||||
{
|
||||
closeTcs.TrySetResult(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
closeTcs.TrySetException(e);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
var receiveTcs = new TaskCompletionSource<int>();
|
||||
|
||||
try
|
||||
{
|
||||
hubConnection.On<int>("Foo", r => { });
|
||||
await hubConnection.StartAsync();
|
||||
hubConnection.On<int>("Foo", r => { receiveTcs.SetResult(r); });
|
||||
await hubConnection.StartAsync().OrTimeout();
|
||||
|
||||
await connection.ReceiveJsonMessage(
|
||||
new
|
||||
|
|
@ -198,12 +181,20 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
|||
arguments = new object[] { "xxx" }
|
||||
}).OrTimeout();
|
||||
|
||||
var ex = await Assert.ThrowsAsync<FormatException>(async () => await closeTcs.Task.OrTimeout());
|
||||
await connection.ReceiveJsonMessage(
|
||||
new
|
||||
{
|
||||
invocationId = "2",
|
||||
type = 1,
|
||||
target = "Foo",
|
||||
arguments = new object[] { 42 }
|
||||
}).OrTimeout();
|
||||
|
||||
Assert.Equal(42, await receiveTcs.Task.OrTimeout());
|
||||
}
|
||||
finally
|
||||
{
|
||||
await hubConnection.DisposeAsync().OrTimeout();
|
||||
await connection.DisposeAsync().OrTimeout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -443,7 +443,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
|||
using (var ms = new MemoryStream())
|
||||
{
|
||||
new MessagePackHubProtocol()
|
||||
.WriteMessage(new InvocationMessage("1", true, "MyMethod", 42), ms);
|
||||
.WriteMessage(new InvocationMessage("1", true, "MyMethod", null, 42), ms);
|
||||
|
||||
var invokeMessage = Convert.ToBase64String(ms.ToArray());
|
||||
var payloadSize = invokeMessage.Length.ToString(CultureInfo.InvariantCulture);
|
||||
|
|
|
|||
|
|
@ -17,14 +17,14 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
|
|||
{
|
||||
public static IEnumerable<object[]> ProtocolTestData => new[]
|
||||
{
|
||||
new object[] { new InvocationMessage("123", true, "Target", 1, "Foo", 2.0f), true, NullValueHandling.Ignore, "{\"invocationId\":\"123\",\"type\":1,\"target\":\"Target\",\"nonBlocking\":true,\"arguments\":[1,\"Foo\",2.0]}" },
|
||||
new object[] { new InvocationMessage("123", false, "Target", 1, "Foo", 2.0f), true, NullValueHandling.Ignore, "{\"invocationId\":\"123\",\"type\":1,\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}" },
|
||||
new object[] { new InvocationMessage("123", false, "Target", true), true, NullValueHandling.Ignore, "{\"invocationId\":\"123\",\"type\":1,\"target\":\"Target\",\"arguments\":[true]}" },
|
||||
new object[] { new InvocationMessage("123", false, "Target", new object[] { null }), true, NullValueHandling.Ignore, "{\"invocationId\":\"123\",\"type\":1,\"target\":\"Target\",\"arguments\":[null]}" },
|
||||
new object[] { new InvocationMessage("123", false, "Target", new CustomObject()), false, NullValueHandling.Ignore, "{\"invocationId\":\"123\",\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00\",\"ByteArrProp\":\"AQID\"}]}" },
|
||||
new object[] { new InvocationMessage("123", false, "Target", new CustomObject()), true, NullValueHandling.Ignore, "{\"invocationId\":\"123\",\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00\",\"byteArrProp\":\"AQID\"}]}" },
|
||||
new object[] { new InvocationMessage("123", false, "Target", new CustomObject()), false, NullValueHandling.Include, "{\"invocationId\":\"123\",\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}" },
|
||||
new object[] { new InvocationMessage("123", false, "Target", new CustomObject()), true, NullValueHandling.Include, "{\"invocationId\":\"123\",\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}" },
|
||||
new object[] { new InvocationMessage("123", true, "Target", null, 1, "Foo", 2.0f), true, NullValueHandling.Ignore, "{\"invocationId\":\"123\",\"type\":1,\"target\":\"Target\",\"nonBlocking\":true,\"arguments\":[1,\"Foo\",2.0]}" },
|
||||
new object[] { new InvocationMessage("123", false, "Target", null, 1, "Foo", 2.0f), true, NullValueHandling.Ignore, "{\"invocationId\":\"123\",\"type\":1,\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}" },
|
||||
new object[] { new InvocationMessage("123", false, "Target", null, true), true, NullValueHandling.Ignore, "{\"invocationId\":\"123\",\"type\":1,\"target\":\"Target\",\"arguments\":[true]}" },
|
||||
new object[] { new InvocationMessage("123", false, "Target", null, new object[] { null }), true, NullValueHandling.Ignore, "{\"invocationId\":\"123\",\"type\":1,\"target\":\"Target\",\"arguments\":[null]}" },
|
||||
new object[] { new InvocationMessage("123", false, "Target", null, new CustomObject()), false, NullValueHandling.Ignore, "{\"invocationId\":\"123\",\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00\",\"ByteArrProp\":\"AQID\"}]}" },
|
||||
new object[] { new InvocationMessage("123", false, "Target", null, new CustomObject()), true, NullValueHandling.Ignore, "{\"invocationId\":\"123\",\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00\",\"byteArrProp\":\"AQID\"}]}" },
|
||||
new object[] { new InvocationMessage("123", false, "Target", null, new CustomObject()), false, NullValueHandling.Include, "{\"invocationId\":\"123\",\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}" },
|
||||
new object[] { new InvocationMessage("123", false, "Target", null, new CustomObject()), true, NullValueHandling.Include, "{\"invocationId\":\"123\",\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}" },
|
||||
|
||||
new object[] { new StreamItemMessage("123", 1), true, NullValueHandling.Ignore, "{\"invocationId\":\"123\",\"type\":2,\"item\":1}" },
|
||||
new object[] { new StreamItemMessage("123", "Foo"), true, NullValueHandling.Ignore, "{\"invocationId\":\"123\",\"type\":2,\"item\":\"Foo\"}" },
|
||||
|
|
@ -114,6 +114,9 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
|
|||
|
||||
[InlineData("{'type':9}", "Unknown message type: 9")]
|
||||
[InlineData("{'type':'foo'}", "Expected 'type' to be of type Integer.")]
|
||||
|
||||
[InlineData("{'type':1,'invocationId':'42','target':'foo','arguments':[42, 'foo'],'nonBlocking':42}", "Expected 'nonBlocking' to be of type Boolean.")]
|
||||
[InlineData("{'type':3,'invocationId':'42','error':'foo','result':true}", "The 'error' and 'result' properties are mutually exclusive.")]
|
||||
public void InvalidMessages(string input, string expectedMessage)
|
||||
{
|
||||
input = Frame(input);
|
||||
|
|
@ -126,15 +129,15 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
|
|||
|
||||
[Theory]
|
||||
[InlineData("{'type':1,'invocationId':'42','target':'foo','arguments':[]}", "Invocation provides 0 argument(s) but target expects 2.")]
|
||||
[InlineData("{'type':1,'invocationId':'42','target':'foo','arguments':[42, 'foo'],'nonBlocking':42}", "Expected 'nonBlocking' to be of type Boolean.")]
|
||||
[InlineData("{'type':3,'invocationId':'42','error':'foo','result':true}", "The 'error' and 'result' properties are mutually exclusive.")]
|
||||
public void InvalidMessagesWithBinder(string input, string expectedMessage)
|
||||
[InlineData("{'type':1,'invocationId':'42','target':'foo','arguments':[ 'abc', 'xyz']}", "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.")]
|
||||
public void ArgumentBindingErrors(string input, string expectedMessage)
|
||||
{
|
||||
input = Frame(input);
|
||||
|
||||
var binder = new TestBinder(paramTypes: new[] { typeof(int), typeof(string) }, returnType: typeof(bool));
|
||||
var protocol = new JsonHubProtocol();
|
||||
var ex = Assert.Throws<FormatException>(() => protocol.TryParseMessages(Encoding.UTF8.GetBytes(input), binder, out var messages));
|
||||
protocol.TryParseMessages(Encoding.UTF8.GetBytes(input), binder, out var messages);
|
||||
var ex = Assert.Throws<FormatException>(() => ((InvocationMessage)messages[0]).Arguments);
|
||||
Assert.Equal(expectedMessage, ex.Message);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
|
|||
|
||||
public static IEnumerable<object[]> TestMessages => new[]
|
||||
{
|
||||
new object[] { new[] { new InvocationMessage("xyz", /*nonBlocking*/ false, "method") } },
|
||||
new object[] { new[] { new InvocationMessage("xyz", /*nonBlocking*/ true, "method") } },
|
||||
new object[] { new[] { new InvocationMessage("xyz", /*nonBlocking*/ true, "method", new object[] { null }) } },
|
||||
new object[] { new[] { new InvocationMessage("xyz", /*nonBlocking*/ true, "method", 42) } },
|
||||
new object[] { new[] { new InvocationMessage("xyz", /*nonBlocking*/ true, "method", 42, "string") } },
|
||||
new object[] { new[] { new InvocationMessage("xyz", /*nonBlocking*/ true, "method", 42, "string", new CustomObject()) } },
|
||||
new object[] { new[] { new InvocationMessage("xyz", /*nonBlocking*/ true, "method", new[] { new CustomObject(), new CustomObject() }) } },
|
||||
new object[] { new[] { new InvocationMessage("xyz", /*nonBlocking*/ false, "method", null) } },
|
||||
new object[] { new[] { new InvocationMessage("xyz", /*nonBlocking*/ true, "method", null) } },
|
||||
new object[] { new[] { new InvocationMessage("xyz", /*nonBlocking*/ true, "method", null, new object[] { null }) } },
|
||||
new object[] { new[] { new InvocationMessage("xyz", /*nonBlocking*/ true, "method", null, 42) } },
|
||||
new object[] { new[] { new InvocationMessage("xyz", /*nonBlocking*/ true, "method", null, 42, "string") } },
|
||||
new object[] { new[] { new InvocationMessage("xyz", /*nonBlocking*/ true, "method", null, 42, "string", new CustomObject()) } },
|
||||
new object[] { new[] { new InvocationMessage("xyz", /*nonBlocking*/ true, "method", null, new[] { new CustomObject(), new CustomObject() }) } },
|
||||
|
||||
new object[] { new[] { new CompletionMessage("xyz", error: "Error not found!", result: null, hasResult: false) } },
|
||||
new object[] { new[] { new CompletionMessage("xyz", error: null, result: null, hasResult: false) } },
|
||||
|
|
@ -52,7 +52,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
|
|||
{
|
||||
new HubMessage[]
|
||||
{
|
||||
new InvocationMessage("xyz", /*nonBlocking*/ true, "method", 42, "string", new CustomObject()),
|
||||
new InvocationMessage("xyz", /*nonBlocking*/ true, "method", null, 42, "string", new CustomObject()),
|
||||
new CompletionMessage("xyz", error: null, result: 42, hasResult: true),
|
||||
new StreamItemMessage("xyz", null),
|
||||
new CompletionMessage("xyz", error: null, result: new CustomObject(), hasResult: true),
|
||||
|
|
@ -93,12 +93,6 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
|
|||
new object[] { new byte[] { 0x95, 0x01, 0xa3, 0x78, 0x79, 0x7a, 0xc2 }, "Reading 'target' as String failed." }, // target missing
|
||||
new object[] { new byte[] { 0x95, 0x01, 0xa3, 0x78, 0x79, 0x7a, 0xc2, 0x00 }, "Reading 'target' as String failed." }, // 0x00 is Int
|
||||
new object[] { new byte[] { 0x95, 0x01, 0xa3, 0x78, 0x79, 0x7a, 0xc2, 0xa1 }, "Reading 'target' as String failed." }, // string is cut
|
||||
new object[] { new byte[] { 0x95, 0x01, 0xa3, 0x78, 0x79, 0x7a, 0xc2, 0xa1, 0x78 }, "Reading array length for 'arguments' failed." }, // array is missing
|
||||
new object[] { new byte[] { 0x95, 0x01, 0xa3, 0x78, 0x79, 0x7a, 0xc2, 0xa1, 0x78, 0x00 }, "Reading array length for 'arguments' failed." }, // 0x00 is not array marker
|
||||
new object[] { new byte[] { 0x95, 0x01, 0xa3, 0x78, 0x79, 0x7a, 0xc2, 0xa1, 0x78, 0x91 }, "Deserializing object of the `String` type for 'argument' failed." }, // array is missing elements
|
||||
new object[] { new byte[] { 0x95, 0x01, 0xa3, 0x78, 0x79, 0x7a, 0xc2, 0xa1, 0x78, 0x91, 0xa2, 0x78 }, "Deserializing object of the `String` type for 'argument' failed." }, // array element is cut
|
||||
new object[] { new byte[] { 0x95, 0x01, 0xa3, 0x78, 0x79, 0x7a, 0xc2, 0xa1, 0x78, 0x92, 0xa0, 0x00 }, "Target method expects 1 arguments(s) but invocation has 2 argument(s)." }, // argument count does not match binder argument count
|
||||
new object[] { new byte[] { 0x95, 0x01, 0xa3, 0x78, 0x79, 0x7a, 0xc2, 0xa1, 0x78, 0x91, 0x00 }, "Deserializing object of the `String` type for 'argument' failed." }, // argument type mismatch
|
||||
|
||||
// StreamItemMessage
|
||||
new object[] { new byte[] { 0x93, 0x02 }, "Reading 'invocationId' as String failed." }, // 0xc2 is Bool false
|
||||
|
|
@ -136,6 +130,35 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
|
|||
Assert.Equal(expectedExceptionMessage, exception.Message);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> ArgumentBindingErrors => new[]
|
||||
{
|
||||
new object[] { new byte[] { 0x95, 0x01, 0xa3, 0x78, 0x79, 0x7a, 0xc2, 0xa1, 0x78 }, "Reading array length for 'arguments' failed." }, // array is missing
|
||||
new object[] { new byte[] { 0x95, 0x01, 0xa3, 0x78, 0x79, 0x7a, 0xc2, 0xa1, 0x78, 0x00 }, "Reading array length for 'arguments' failed." }, // 0x00 is not array marker
|
||||
new object[] { new byte[] { 0x95, 0x01, 0xa3, 0x78, 0x79, 0x7a, 0xc2, 0xa1, 0x78, 0x91 }, "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked." }, // array is missing elements
|
||||
new object[] { new byte[] { 0x95, 0x01, 0xa3, 0x78, 0x79, 0x7a, 0xc2, 0xa1, 0x78, 0x91, 0xa2, 0x78 }, "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked." }, // array element is cut
|
||||
new object[] { new byte[] { 0x95, 0x01, 0xa3, 0x78, 0x79, 0x7a, 0xc2, 0xa1, 0x78, 0x92, 0xa0, 0x00 }, "Invocation provides 2 argument(s) but target expects 1." }, // argument count does not match binder argument count
|
||||
new object[] { new byte[] { 0x95, 0x01, 0xa3, 0x78, 0x79, 0x7a, 0xc2, 0xa1, 0x78, 0x91, 0x00 }, "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked." }, // argument type mismatch
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ArgumentBindingErrors))]
|
||||
public void GettingArgumentsThrowsIfBindingFailed(byte[] payload, string expectedExceptionMessage)
|
||||
{
|
||||
var payloadSize = payload.Length;
|
||||
Debug.Assert(payloadSize <= 0x7f, "This test does not support payloads larger than 127 bytes");
|
||||
|
||||
// prefix payload with the size
|
||||
var buffer = new byte[1 + payloadSize];
|
||||
buffer[0] = (byte)(payloadSize & 0x7f);
|
||||
Array.Copy(payload, 0, buffer, 1, payloadSize);
|
||||
|
||||
var binder = new TestBinder(new[] { typeof(string) }, typeof(string));
|
||||
_hubProtocol.TryParseMessages(buffer, binder, out var messages);
|
||||
var exception = Assert.Throws<FormatException>(() => ((InvocationMessage)messages[0]).Arguments);
|
||||
|
||||
Assert.Equal(expectedExceptionMessage, exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(new object[] { new byte[] { 0x05, 0x01 }, 0 })]
|
||||
[InlineData(new object[] {
|
||||
|
|
@ -157,7 +180,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
|
|||
{
|
||||
new object[]
|
||||
{
|
||||
new InvocationMessage("0", false, "A", 1, new CustomObject()),
|
||||
new InvocationMessage("0", false, "A", null, 1, new CustomObject()),
|
||||
new byte[]
|
||||
{
|
||||
0x6c, 0x95, 0x01, 0xa1, 0x30, 0xc2, 0xa1, 0x41,
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
|||
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, WindowsVersions.Win2008R2, SkipReason = "No WebSockets Client for this platform")]
|
||||
public async Task HTTPRequestsNotSentWhenWebSocketsTransportRequested()
|
||||
{
|
||||
using (StartLog(out var loggerFactory, testName: nameof(HTTPRequestsNotSentWhenWebSocketsTransportRequested)))
|
||||
using (StartLog(out var loggerFactory))
|
||||
{
|
||||
var logger = loggerFactory.CreateLogger<EndToEndTests>();
|
||||
var url = _serverFixture.BaseUrl + "/echo";
|
||||
|
|
|
|||
Loading…
Reference in New Issue