private async Task <bool> ValidateInvocationMode(HubMethodDescriptor hubMethodDescriptor, bool isStreamResponse, HubMethodInvocationMessage hubMethodInvocationMessage, HubConnectionContext connection) { if (hubMethodDescriptor.IsStreamResponse && !isStreamResponse) { // Non-null/empty InvocationId? Blocking if (!string.IsNullOrEmpty(hubMethodInvocationMessage.InvocationId)) { Log.StreamingMethodCalledWithInvoke(_logger, hubMethodInvocationMessage); await connection.WriteAsync(CompletionMessage.WithError(hubMethodInvocationMessage.InvocationId, $"The client attempted to invoke the streaming '{hubMethodInvocationMessage.Target}' method with a non-streaming invocation.")); } return(false); } if (!hubMethodDescriptor.IsStreamResponse && isStreamResponse) { Log.NonStreamingMethodCalledWithStream(_logger, hubMethodInvocationMessage); await connection.WriteAsync(CompletionMessage.WithError(hubMethodInvocationMessage.InvocationId, $"The client attempted to invoke the non-streaming '{hubMethodInvocationMessage.Target}' method with a streaming invocation.")); return(false); } return(true); }
private async Task ProcessInvocation(HubConnectionContext connection, HubMethodInvocationMessage hubMethodInvocationMessage, bool isStreamedInvocation) { try { // If an unexpected exception occurs then we want to kill the entire connection // by ending the processing loop if (!_methods.TryGetValue(hubMethodInvocationMessage.Target, out var descriptor)) { // Send an error to the client. Then let the normal completion process occur Log.UnknownHubMethod(_logger, hubMethodInvocationMessage.Target); await connection.WriteAsync(CompletionMessage.WithError( hubMethodInvocationMessage.InvocationId, $"Unknown hub method '{hubMethodInvocationMessage.Target}'")); } else { await Invoke(descriptor, connection, hubMethodInvocationMessage, isStreamedInvocation); } } catch (Exception ex) { // Abort the entire connection if the invocation fails in an unexpected way connection.Abort(ex); } }
public async Task GetMessages_LastMessageId_InvocationMessageIsSent() { var sessionId = Guid.NewGuid(); await using var fixture = new PlanningPokerSignalRClientFixture(); var resultTask = fixture.Target.GetMessages(PlanningPokerData.TeamName, PlanningPokerData.MemberName, sessionId, 2157483849, fixture.CancellationToken); var sentMessage = await fixture.GetSentMessage(); Assert.IsNotNull(sentMessage); Assert.IsInstanceOfType(sentMessage, typeof(InvocationMessage)); var sentInvocationMessage = (InvocationMessage)sentMessage; Assert.AreEqual(RequestName, sentInvocationMessage.Target); var expectedArguments = new object[] { PlanningPokerData.TeamName, PlanningPokerData.MemberName, sessionId, 2157483849L }; CollectionAssert.AreEqual(expectedArguments, sentInvocationMessage.Arguments); var notifyMessage = new InvocationMessage(ResponseName, new object[] { new List <Message>() }); await fixture.ReceiveMessage(notifyMessage); var returnMessage = new CompletionMessage(sentInvocationMessage.InvocationId, null, null, false); await fixture.ReceiveMessage(returnMessage); await resultTask; // Ensure asynchronous Dispose. Otherwise, Dispose is executed in OnNotify handler and it causes deadlock. await Task.Yield(); }
private async Task Invoke(HubMethodDescriptor descriptor, HubConnectionContext connection, HubMethodInvocationMessage hubMethodInvocationMessage, bool isStreamedInvocation) { var methodExecutor = descriptor.MethodExecutor; using (var scope = _serviceScopeFactory.CreateScope()) { if (!await IsHubMethodAuthorized(scope.ServiceProvider, connection.User, descriptor.Policies)) { _logger.HubMethodNotAuthorized(hubMethodInvocationMessage.Target); await SendInvocationError(hubMethodInvocationMessage, connection, $"Failed to invoke '{hubMethodInvocationMessage.Target}' because user is unauthorized"); return; } if (!await ValidateInvocationMode(methodExecutor.MethodReturnType, isStreamedInvocation, hubMethodInvocationMessage, connection)) { return; } var hubActivator = scope.ServiceProvider.GetRequiredService <IHubActivator <THub> >(); var hub = hubActivator.Create(); try { InitializeHub(hub, connection); var result = await ExecuteHubMethod(methodExecutor, hub, hubMethodInvocationMessage.Arguments); if (isStreamedInvocation) { var enumerator = GetStreamingEnumerator(connection, hubMethodInvocationMessage.InvocationId, methodExecutor, result, methodExecutor.MethodReturnType); _logger.StreamingResult(hubMethodInvocationMessage.InvocationId, methodExecutor.MethodReturnType.FullName); await StreamResultsAsync(hubMethodInvocationMessage.InvocationId, connection, enumerator); } // Non-empty/null InvocationId ==> Blocking invocation that needs a response else if (!string.IsNullOrEmpty(hubMethodInvocationMessage.InvocationId)) { _logger.SendingResult(hubMethodInvocationMessage.InvocationId, methodExecutor.MethodReturnType.FullName); await SendMessageAsync(connection, CompletionMessage.WithResult(hubMethodInvocationMessage.InvocationId, result)); } } catch (TargetInvocationException ex) { _logger.FailedInvokingHubMethod(hubMethodInvocationMessage.Target, ex); await SendInvocationError(hubMethodInvocationMessage, connection, ex.InnerException.Message); } catch (Exception ex) { _logger.FailedInvokingHubMethod(hubMethodInvocationMessage.Target, ex); await SendInvocationError(hubMethodInvocationMessage, connection, ex.Message); } finally { hubActivator.Release(hub); } } }
private bool CompletionMessagesEqual(CompletionMessage x, CompletionMessage y) { return(SequenceEqual(x.Headers, y.Headers) && string.Equals(x.InvocationId, y.InvocationId, StringComparison.Ordinal) && string.Equals(x.Error, y.Error, StringComparison.Ordinal) && x.HasResult == y.HasResult && (Equals(x.Result, y.Result) || SequenceEqual(x.Result, y.Result))); }
public void SerializerCanSerializeTypesWithNoDefaultCtor() { var result = Write(CompletionMessage.WithResult("0", new List <int> { 42 }.AsReadOnly())); AssertMessages(new byte[] { ArrayBytes(5), 3, 0x80, StringBytes(1), (byte)'0', 0x03, ArrayBytes(1), 42 }, result); }
public void Complete(CompletionMessage message) { _lookup.TryRemove(message.InvocationId, out var converter); if (converter == null) { throw new KeyNotFoundException($"No stream with id '{message.InvocationId}' could be found."); } converter.TryComplete(message.HasResult || message.Error == null ? null : new Exception(message.Error)); }
private async Task SendInvocationError(string invocationId, HubConnectionContext connection, string errorMessage) { if (string.IsNullOrEmpty(invocationId)) { return; } await connection.WriteAsync(CompletionMessage.WithError(invocationId, errorMessage)); }
private async Task SendInvocationError(HubMethodInvocationMessage hubMethodInvocationMessage, HubConnectionContext connection, string errorMessage) { if (string.IsNullOrEmpty(hubMethodInvocationMessage.InvocationId)) { return; } await SendMessageAsync(connection, CompletionMessage.WithError(hubMethodInvocationMessage.InvocationId, errorMessage)); }
public bool TryComplete(CompletionMessage message) { _lookup.TryRemove(message.InvocationId !, out var converter); if (converter == null) { return(false); } converter.TryComplete(message.HasResult || message.Error == null ? null : new Exception(message.Error)); return(true); }
public override async Task <HttpResponseMessage> ExecuteAsync(HttpRequestMessage request) { if (!Resolver.TryGetInvocationContext(request, out var context)) { //TODO: More detailed exception throw new SignalRTriggerException(); } var(message, protocol) = await Resolver.GetMessageAsync <InvocationMessage>(request).ConfigureAwait(false); AssertConsistency(context, message); context.Arguments = message.Arguments; // Only when it's an invoke, we need the result from function execution. TaskCompletionSource <object> tcs = null; if (!string.IsNullOrEmpty(message.InvocationId)) { tcs = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously); } HttpResponseMessage response; CompletionMessage completionMessage = null; var functionResult = await ExecuteWithAuthAsync(request, ExecutionContext, context, tcs).ConfigureAwait(false); if (tcs != null) { if (!functionResult.Succeeded) { var errorMessage = functionResult.Exception?.InnerException?.Message ?? functionResult.Exception?.Message ?? "Method execution failed."; completionMessage = CompletionMessage.WithError(message.InvocationId, errorMessage); response = new HttpResponseMessage(HttpStatusCode.OK); } else { var result = await tcs.Task.ConfigureAwait(false); completionMessage = CompletionMessage.WithResult(message.InvocationId, result); response = new HttpResponseMessage(HttpStatusCode.OK); } } else { response = new HttpResponseMessage(HttpStatusCode.OK); } if (completionMessage != null) { response.Content = new ByteArrayContent(protocol.GetMessageBytes(completionMessage).ToArray()); } return(response); }
public override void Complete(CompletionMessage completionMessage) { if (!string.IsNullOrEmpty(completionMessage.Error)) { Fail(new HubException(completionMessage.Error)); return; } Log.InvocationCompleted(Logger, InvocationId); _completionSource.TrySetResult(completionMessage.Result); }
private async Task OnConnectedAsync(HubInvocationMessage message) { var connection = CreateHubConnectionContext(message); _connections.Add(connection); await _lifetimeManager.OnConnectedAsync(connection); await _hubInvoker.OnConnectedAsync(connection); await SendMessageAsync(CompletionMessage.WithResult(message.InvocationId, "")); }
public void CanWriteObjectsWithoutDefaultCtors() { var expectedPayload = new byte[] { 0x07, 0x94, 0x03, 0xa1, 0x30, 0x03, 0x91, 0x2a }; using (var memoryStream = new MemoryStream()) { _hubProtocol.WriteMessage(CompletionMessage.WithResult("0", new List <int> { 42 }.AsReadOnly()), memoryStream); Assert.Equal(expectedPayload, memoryStream.ToArray()); } }
private Task ProcessStreamBindingFailure(HubConnectionContext connection, StreamBindingFailureMessage bindingFailureMessage) { var errorString = ErrorMessageHelper.BuildErrorMessage( "Failed to bind Stream message.", bindingFailureMessage.BindingFailure.SourceException, _enableDetailedErrors); var message = CompletionMessage.WithError(bindingFailureMessage.Id, errorString); Log.ClosingStreamWithBindingError(_logger, message); connection.StreamTracker.Complete(message); return(Task.CompletedTask); }
private async Task OnCompletionAsync(CompletionMessage message) { var connection = GetHubConnectionContext(message); if (connection == null) { await SendMessageAsync(CompletionMessage.WithError(message.InvocationId, "No connection found.")); return; } await _hubInvoker.OnCompletionAsync(connection, message); }
private async Task Execute(HubConnectionContext connection, InvocationMessage invocationMessage) { if (!_methods.TryGetValue(invocationMessage.Target, out var descriptor)) { // Send an error to the client. Then let the normal completion process occur _logger.UnknownHubMethod(invocationMessage.Target); await SendMessageAsync(connection, CompletionMessage.WithError(invocationMessage.InvocationId, $"Unknown hub method '{invocationMessage.Target}'")); } else { await Invoke(descriptor, connection, invocationMessage); } }
private void DispatchInvocationCompletion(CompletionMessage completion, InvocationRequest irq) { _logger.ReceivedInvocationCompletion(completion.InvocationId); if (irq.CancellationToken.IsCancellationRequested) { _logger.CancelingInvocationCompletion(irq.InvocationId); } else { irq.Complete(completion); } }
private void WriteCompletionMessage(CompletionMessage message, JsonTextWriter writer) { WriteInvocationId(message, writer); if (!string.IsNullOrEmpty(message.Error)) { writer.WritePropertyName(ErrorPropertyName); writer.WriteValue(message.Error); } else if (message.HasResult) { writer.WritePropertyName(ResultPropertyName); PayloadSerializer.Serialize(writer, message.Result); } }
private async Task SendInvocationError(InvocationMessage invocationMessage, HubConnectionContext connection, Type returnType, Exception ex) { if (!invocationMessage.NonBlocking) { if (IsIObservable(returnType) || IsChannel(returnType, out _)) { await SendMessageAsync(connection, new StreamCompletionMessage(invocationMessage.InvocationId, ex.Message)); } else { await SendMessageAsync(connection, CompletionMessage.WithError(invocationMessage.InvocationId, ex.Message)); } } }
private ValueTask CleanupInvocation(HubConnectionContext connection, HubMethodInvocationMessage hubMessage, IHubActivator <THub> hubActivator, THub hub, IServiceScope scope) { if (hubMessage.StreamIds != null) { foreach (var stream in hubMessage.StreamIds) { connection.StreamTracker.TryComplete(CompletionMessage.Empty(stream)); } } hubActivator?.Release(hub); return(scope.DisposeAsync()); }
private Task ProcessInvocation(HubConnectionContext connection, HubMethodInvocationMessage hubMethodInvocationMessage, bool isStreamedInvocation) { if (!_methods.TryGetValue(hubMethodInvocationMessage.Target, out var descriptor)) { // Send an error to the client. Then let the normal completion process occur Log.UnknownHubMethod(_logger, hubMethodInvocationMessage.Target); return(connection.WriteAsync(CompletionMessage.WithError( hubMethodInvocationMessage.InvocationId, $"Unknown hub method '{hubMethodInvocationMessage.Target}'")).AsTask()); } else { return(Invoke(descriptor, connection, hubMethodInvocationMessage, isStreamedInvocation)); } }
private Task ProcessStreamBindingFailure(HubConnectionContext connection, StreamBindingFailureMessage bindingFailureMessage) { var errorString = ErrorMessageHelper.BuildErrorMessage( "Failed to bind Stream message.", bindingFailureMessage.BindingFailure.SourceException, _enableDetailedErrors); var message = CompletionMessage.WithError(bindingFailureMessage.Id, errorString); Log.ClosingStreamWithBindingError(_logger, message); // ignore failure, it means the client already completed the stream or the stream never existed on the server connection.StreamTracker.TryComplete(message); // TODO: Send stream completion message to client when we add it return(Task.CompletedTask); }
private void WriteCompletionMessage(CompletionMessage message, JsonTextWriter writer) { writer.WriteStartObject(); WriteHubInvocationMessageCommon(message, writer, HubProtocolConstants.CompletionMessageType); if (!string.IsNullOrEmpty(message.Error)) { writer.WritePropertyName(ErrorPropertyName); writer.WriteValue(message.Error); } else if (message.HasResult) { writer.WritePropertyName(ResultPropertyName); PayloadSerializer.Serialize(writer, message.Result); } writer.WriteEndObject(); }
private async Task StreamResultsAsync(string invocationId, HubConnectionContext connection, IAsyncEnumerator <object> enumerator, IServiceScope scope, IHubActivator <THub> hubActivator, THub hub, CancellationTokenSource streamCts) { string error = null; using (scope) { try { while (await enumerator.MoveNextAsync()) { // Send the stream item await connection.WriteAsync(new StreamItemMessage(invocationId, enumerator.Current)); } } catch (ChannelClosedException ex) { // If the channel closes from an exception in the streaming method, grab the innerException for the error from the streaming method error = ErrorMessageHelper.BuildErrorMessage("An error occurred on the server while streaming results.", ex.InnerException ?? ex, _enableDetailedErrors); } catch (Exception ex) { // If the streaming method was canceled we don't want to send a HubException message - this is not an error case if (!(ex is OperationCanceledException && connection.ActiveRequestCancellationSources.TryGetValue(invocationId, out var cts) && cts.IsCancellationRequested)) { error = ErrorMessageHelper.BuildErrorMessage("An error occurred on the server while streaming results.", ex, _enableDetailedErrors); } } finally { (enumerator as IDisposable)?.Dispose(); hubActivator.Release(hub); // Dispose the linked CTS for the stream. streamCts.Dispose(); await connection.WriteAsync(CompletionMessage.WithError(invocationId, error)); if (connection.ActiveRequestCancellationSources.TryRemove(invocationId, out var cts)) { cts.Dispose(); } } } }
public async Task JoinTeam_TeamDoesNotExist_PlanningPokerException() { await using var fixture = new PlanningPokerSignalRClientFixture(); var resultTask = fixture.Target.JoinTeam(PlanningPokerData.TeamName, PlanningPokerData.MemberName, false, fixture.CancellationToken); var sentMessage = await fixture.GetSentMessage(); var invocationId = GetInvocationId(sentMessage); var returnMessage = new CompletionMessage(invocationId, "Team 'Test team' does not exist.", null, false); await fixture.ReceiveMessage(returnMessage); var exception = await Assert.ThrowsExceptionAsync <PlanningPokerException>(() => resultTask); Assert.AreEqual("Team 'Test team' does not exist.", exception.Message); }
public async Task JoinTeam_ReturnsEmptyErrorMessage_PlanningPokerException() { await using var fixture = new PlanningPokerSignalRClientFixture(); var resultTask = fixture.Target.JoinTeam(PlanningPokerData.TeamName, PlanningPokerData.MemberName, false, fixture.CancellationToken); var sentMessage = await fixture.GetSentMessage(); var invocationId = GetInvocationId(sentMessage); var returnMessage = new CompletionMessage(invocationId, "An unexpected error occured. HubException: ", null, false); await fixture.ReceiveMessage(returnMessage); var exception = await Assert.ThrowsExceptionAsync <PlanningPokerException>(() => resultTask); Assert.AreEqual(string.Empty, exception.Message); }
private async Task StreamResultsAsync(string invocationId, HubConnectionContext connection, IAsyncEnumerator <object> enumerator) { try { while (await enumerator.MoveNextAsync()) { // Send the stream item await SendMessageAsync(connection, new StreamItemMessage(invocationId, enumerator.Current)); } await SendMessageAsync(connection, CompletionMessage.Empty(invocationId)); } catch (Exception ex) { await SendMessageAsync(connection, CompletionMessage.WithError(invocationId, ex.Message)); } }
public override void Complete(CompletionMessage completionMessage) { Log.InvocationCompleted(Logger, InvocationId); if (completionMessage.Result != null) { Log.ReceivedUnexpectedComplete(Logger, InvocationId); _channel.Writer.TryComplete(new InvalidOperationException("Server provided a result in a completion response to a streamed invocation.")); } if (!string.IsNullOrEmpty(completionMessage.Error)) { Fail(new HubException(completionMessage.Error)); return; } _channel.Writer.TryComplete(); }
public async Task CreateTeam_TeamNameExists_PlanningPokerException() { await using var fixture = new PlanningPokerSignalRClientFixture(); var resultTask = fixture.Target.CreateTeam(PlanningPokerData.TeamName, PlanningPokerData.ScrumMasterName, Deck.Standard, fixture.CancellationToken); var sentMessage = await fixture.GetSentMessage(); var invocationId = GetInvocationId(sentMessage); var returnMessage = new CompletionMessage(invocationId, "An unexpected error occured. HubException: Team 'Test team' already exists.", null, false); await fixture.ReceiveMessage(returnMessage); var exception = await Assert.ThrowsExceptionAsync <PlanningPokerException>(() => resultTask); Assert.AreEqual("Team 'Test team' already exists.", exception.Message); }