Handling exceptions thrown when invoking a hub method (#332)

This commit is contained in:
Pawel Kadluczka 2017-03-22 11:06:47 -07:00 committed by GitHub
parent 5642f09a83
commit e955c4b9aa
2 changed files with 45 additions and 16 deletions

View File

@ -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 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);
// 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);
}
var ms = new MemoryStream();
await _adapter.WriteMessageAsync(descriptor, ms, cancellationToken);
try
{
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 HIGH: Handle return value/Exception from SendAsync
await _connection.SendAsync(ms.ToArray(), MessageType.Text, cancellationToken);
_logger.LogInformation("Sending Invocation #{0} complete", descriptor.Id);
// TODO: Format.Text - who, where and when decides about the format of outgoing messages
await _connection.SendAsync(ms.ToArray(), MessageType.Text, cancellationToken);
_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 await irq.Completion.Task;
@ -294,7 +303,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
public InvocationRequest(CancellationToken cancellationToken, Type resultType)
{
var tcs = new TaskCompletionSource<object>();
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
Completion = tcs;
CancellationToken = cancellationToken;
Registration = cancellationToken.Register(() => tcs.TrySetCanceled());

View File

@ -13,6 +13,7 @@ using Microsoft.Extensions.Logging;
using Moq;
using Moq.Protected;
using Xunit;
using System.IO;
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()
{
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 =
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()
{
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();
var exception =
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]