Handling exceptions thrown when invoking a hub method (#332)
This commit is contained in:
parent
5642f09a83
commit
e955c4b9aa
|
|
@ -102,8 +102,6 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
public Task<object> Invoke(string methodName, Type returnType, params object[] args) => Invoke(methodName, returnType, CancellationToken.None, args);
|
public Task<object> Invoke(string methodName, Type returnType, params object[] args) => Invoke(methodName, returnType, CancellationToken.None, args);
|
||||||
public async Task<object> Invoke(string methodName, Type returnType, CancellationToken cancellationToken, params object[] args)
|
public async Task<object> Invoke(string methodName, Type returnType, CancellationToken cancellationToken, params object[] args)
|
||||||
{
|
{
|
||||||
// TODO: we should reject calls to here after the connection is "done" or has not been started (e.g. sending an invocation failed)
|
|
||||||
|
|
||||||
_logger.LogTrace("Preparing invocation of '{0}', with return type '{1}' and {2} args", methodName, returnType.AssemblyQualifiedName, args.Length);
|
_logger.LogTrace("Preparing invocation of '{0}', with return type '{1}' and {2} args", methodName, returnType.AssemblyQualifiedName, args.Length);
|
||||||
|
|
||||||
// Create an invocation descriptor.
|
// Create an invocation descriptor.
|
||||||
|
|
@ -134,15 +132,26 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
_logger.LogTrace("Invocation #{0}: {1} {2}({3})", descriptor.Id, returnType.FullName, methodName, argsList);
|
_logger.LogTrace("Invocation #{0}: {1} {2}({3})", descriptor.Id, returnType.FullName, methodName, argsList);
|
||||||
}
|
}
|
||||||
|
|
||||||
var ms = new MemoryStream();
|
try
|
||||||
await _adapter.WriteMessageAsync(descriptor, ms, cancellationToken);
|
{
|
||||||
|
var ms = new MemoryStream();
|
||||||
|
await _adapter.WriteMessageAsync(descriptor, ms, cancellationToken);
|
||||||
|
|
||||||
_logger.LogInformation("Sending Invocation #{0}", descriptor.Id);
|
_logger.LogInformation("Sending Invocation #{0}", descriptor.Id);
|
||||||
|
|
||||||
// TODO: Format.Text - who, where and when decides about the format of outgoing messages
|
// TODO: Format.Text - who, where and when decides about the format of outgoing messages
|
||||||
// TODO HIGH: Handle return value/Exception from SendAsync
|
await _connection.SendAsync(ms.ToArray(), MessageType.Text, cancellationToken);
|
||||||
await _connection.SendAsync(ms.ToArray(), MessageType.Text, cancellationToken);
|
_logger.LogInformation("Sending Invocation #{0} complete", descriptor.Id);
|
||||||
_logger.LogInformation("Sending Invocation #{0} complete", descriptor.Id);
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(0, ex, "Sending Invocation #{0} failed", descriptor.Id);
|
||||||
|
irq.Completion.TrySetException(ex);
|
||||||
|
lock (_pendingCallsLock)
|
||||||
|
{
|
||||||
|
_pendingCalls.Remove(descriptor.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Return the completion task. It will be completed by ReceiveMessages when the response is received.
|
// Return the completion task. It will be completed by ReceiveMessages when the response is received.
|
||||||
return await irq.Completion.Task;
|
return await irq.Completion.Task;
|
||||||
|
|
@ -294,7 +303,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
|
|
||||||
public InvocationRequest(CancellationToken cancellationToken, Type resultType)
|
public InvocationRequest(CancellationToken cancellationToken, Type resultType)
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<object>();
|
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
Completion = tcs;
|
Completion = tcs;
|
||||||
CancellationToken = cancellationToken;
|
CancellationToken = cancellationToken;
|
||||||
Registration = cancellationToken.Register(() => tcs.TrySetCanceled());
|
Registration = cancellationToken.Register(() => tcs.TrySetCanceled());
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ using Microsoft.Extensions.Logging;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Moq.Protected;
|
using Moq.Protected;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
{
|
{
|
||||||
|
|
@ -92,24 +93,43 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Skip = "Not implemented")]
|
[Fact]
|
||||||
public async Task InvokeThrowsIfHubConnectionNotStarted()
|
public async Task InvokeThrowsIfHubConnectionNotStarted()
|
||||||
{
|
{
|
||||||
var hubConnection = new HubConnection(new Uri("http://fakeuri.org"), Mock.Of<IInvocationAdapter>(), Mock.Of<ILoggerFactory>());
|
var hubConnection = new HubConnection(new Uri("http://fakeuri.org"));
|
||||||
var exception =
|
var exception =
|
||||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await hubConnection.Invoke<int>("test"));
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await hubConnection.Invoke<int>("test"));
|
||||||
Assert.Equal("Cannot invoke methods on non-started connections.", exception.Message);
|
Assert.Equal("Cannot send messages when the connection is not in the Connected state.", exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Skip = "Not implemented")]
|
[Fact]
|
||||||
public async Task InvokeThrowsIfHubConnectionDisposed()
|
public async Task InvokeThrowsIfHubConnectionDisposed()
|
||||||
{
|
{
|
||||||
var hubConnection = new HubConnection(new Uri("http://fakeuri.org"), Mock.Of<IInvocationAdapter>(), Mock.Of<ILoggerFactory>());
|
var hubConnection = new HubConnection(new Uri("http://fakeuri.org"));
|
||||||
await hubConnection.DisposeAsync();
|
await hubConnection.DisposeAsync();
|
||||||
|
|
||||||
var exception =
|
var exception =
|
||||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await hubConnection.Invoke<int>("test"));
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await hubConnection.Invoke<int>("test"));
|
||||||
Assert.Equal("Cannot invoke methods on disposed connections.", exception.Message);
|
Assert.Equal("Cannot send messages when the connection is not in the Connected state.", exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task InvokeThrowsIfSerializingMessageFails()
|
||||||
|
{
|
||||||
|
var mockConnection = new Mock<IConnection>();
|
||||||
|
|
||||||
|
var exception = new InvalidOperationException();
|
||||||
|
var mockInvocationAdapter = new Mock<IInvocationAdapter>();
|
||||||
|
mockInvocationAdapter
|
||||||
|
.Setup(a => a.WriteMessageAsync(It.IsAny<InvocationMessage>(), It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
|
||||||
|
.Returns(Task.FromException(exception));
|
||||||
|
|
||||||
|
var hubConnection = new HubConnection(mockConnection.Object, mockInvocationAdapter.Object, null);
|
||||||
|
await hubConnection.StartAsync(Mock.Of<ITransport>());
|
||||||
|
|
||||||
|
var actualException =
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await hubConnection.Invoke<int>("test"));
|
||||||
|
Assert.Same(exception, actualException);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue