private IAsyncEnumerator <object> GetStreamingEnumerator(CloudHubConnectionContext connection, string invocationId, ObjectMethodExecutor methodExecutor, object result, Type resultType) { if (result != null) { var observableInterface = IsIObservable(resultType) ? resultType : resultType.GetInterfaces().FirstOrDefault(IsIObservable); if (observableInterface != null) { return(AsyncEnumeratorAdapters.FromObservable(result, observableInterface, CreateCancellation())); } if (IsChannel(resultType, out var payloadType)) { return(AsyncEnumeratorAdapters.FromChannel(result, payloadType, CreateCancellation())); } } //Log.InvalidReturnValueFromStreamingMethod(_logger, methodExecutor.MethodInfo.Name); throw new InvalidOperationException($"The value returned by the streaming method '{methodExecutor.MethodInfo.Name}' is null, does not implement the IObservable<> interface or is not a ReadableChannel<>."); CancellationToken CreateCancellation() { var streamCts = new CancellationTokenSource(); connection.ActiveRequestCancellationSources.TryAdd(invocationId, streamCts); return(CancellationTokenSource.CreateLinkedTokenSource(connection.ConnectionAbortedToken, streamCts.Token).Token); } }
private async Task Invoke(HubMethodDescriptor descriptor, CloudHubConnectionContext connection, HubMethodInvocationMessage hubMethodInvocationMessage, bool isStreamedInvocation) { var methodExecutor = descriptor.MethodExecutor; using (var scope = _serviceScopeFactory.CreateScope()) { if (!await IsHubMethodAuthorized(scope.ServiceProvider, connection.User, descriptor.Policies)) { _logger.LogError($"Hub method unauthorized: {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); //Log.StreamingResult(_logger, hubMethodInvocationMessage.InvocationId, methodExecutor); await StreamResultsAsync(hubMethodInvocationMessage.InvocationId, connection, enumerator); } // Non-empty/null InvocationId ==> Blocking invocation that needs a response else if (!string.IsNullOrEmpty(hubMethodInvocationMessage.InvocationId)) { //Log.SendingResult(_logger, hubMethodInvocationMessage.InvocationId, methodExecutor); await connection.ReturnResultAsync(CompletionMessage.WithResult(hubMethodInvocationMessage.InvocationId, result)); } } catch (TargetInvocationException ex) { //Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex); await SendInvocationError(hubMethodInvocationMessage, connection, ex.InnerException.Message); } catch (Exception ex) { //Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex); await SendInvocationError(hubMethodInvocationMessage, connection, ex.Message); } finally { hubActivator.Release(hub); } } }
private async Task StreamResultsAsync(string invocationId, CloudHubConnectionContext connection, IAsyncEnumerator <object> enumerator) { string error = null; try { while (await enumerator.MoveNextAsync()) { // Send the stream item await connection.ReturnResultAsync(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 = ex.InnerException == null ? ex.Message : ex.InnerException.Message; } 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 = ex.Message; } } finally { await connection.ReturnResultAsync(new CompletionMessage(invocationId, error : error, result : null, hasResult : false)); if (connection.ActiveRequestCancellationSources.TryRemove(invocationId, out var cts)) { cts.Dispose(); } } }
private async Task ProcessInvocation(CloudHubConnectionContext 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 _logger.LogWarning($"Unknown hub method: {hubMethodInvocationMessage.Target}"); await SendInvocationError(hubMethodInvocationMessage, connection, $"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); } }