public override void Init(IDependencyResolver resolver)
 {
     _manager         = resolver.GetService <IHubManager>();
     _activator       = resolver.GetService <IHubActivator>();
     _pipelineInvoker = resolver.GetService <IHubPipelineInvoker>();
     base.Init(resolver);
 }
 public DefaultHubManager(IEnumerable<IHubDescriptorProvider> hubProviders,
                          IEnumerable<IMethodDescriptorProvider> methodProviders,
                          IHubActivator activator)
 {
     _hubProviders = hubProviders;
     _methodProviders = methodProviders;
     _activator = activator;
 }
Example #3
0
 public DefaultHubManager(IEnumerable <IHubDescriptorProvider> hubProviders,
                          IEnumerable <IMethodDescriptorProvider> methodProviders,
                          IHubActivator activator)
 {
     _hubProviders    = hubProviders;
     _methodProviders = methodProviders;
     _activator       = activator;
 }
 public DefaultHubFactory(IHubActivator hubActivator)
 {
     if (hubActivator == null)
     {
         throw new ArgumentNullException("hubActivator");
     }
     _hubActivator = hubActivator;
 }
 public DefaultHubFactory(IHubActivator hubActivator)
 {
     if (hubActivator == null)
     {
         throw new ArgumentNullException("hubActivator");
     }
     _hubActivator = hubActivator;
 }
Example #6
0
        public DefaultHubFactory(IHubActivator hubActivator, IHubTypeResolver hubTypeResolver)
        {
            if (hubActivator == null)
            {
                throw new ArgumentNullException("hubActivator");
            }

            if (hubTypeResolver == null)
            {
                throw new ArgumentNullException("hubTypeResolver");
            }

            _hubActivator    = hubActivator;
            _hubTypeResolver = hubTypeResolver;
        }
Example #7
0
        public DefaultHubFactory(IHubActivator hubActivator, IHubTypeResolver hubTypeResolver)
        {
            if (hubActivator == null)
            {
                throw new ArgumentNullException("hubActivator");
            }

            if (hubTypeResolver == null)
            {
                throw new ArgumentNullException("hubTypeResolver");
            }

            _hubActivator = hubActivator;
            _hubTypeResolver = hubTypeResolver;
        }
Example #8
0
        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();
                    }
                }
            }
        }
Example #9
0
 public DefaultHubManager(IDependencyResolver resolver)
 {
     _hubProviders = resolver.ResolveAll<IHubDescriptorProvider>();
     _methodProviders = resolver.ResolveAll<IMethodDescriptorProvider>();
     _activator = resolver.Resolve<IHubActivator>();
 }
Example #10
0
        private async Task Invoke(HubMethodDescriptor descriptor, HubConnectionContext connection,
                                  HubMethodInvocationMessage hubMethodInvocationMessage, bool isStreamResponse, bool isStreamCall)
        {
            var methodExecutor = descriptor.MethodExecutor;

            var disposeScope = true;
            var scope        = _serviceScopeFactory.CreateScope();
            IHubActivator <THub> hubActivator = null;
            THub hub = null;

            try
            {
                if (!await IsHubMethodAuthorized(scope.ServiceProvider, connection.User, descriptor.Policies))
                {
                    Log.HubMethodNotAuthorized(_logger, hubMethodInvocationMessage.Target);
                    await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                              $"Failed to invoke '{hubMethodInvocationMessage.Target}' because user is unauthorized");

                    return;
                }

                if (!await ValidateInvocationMode(descriptor, isStreamResponse, hubMethodInvocationMessage, connection))
                {
                    return;
                }

                hubActivator = scope.ServiceProvider.GetRequiredService <IHubActivator <THub> >();
                hub          = hubActivator.Create();

                if (isStreamCall)
                {
                    // swap out placeholders for channels
                    var args = hubMethodInvocationMessage.Arguments;
                    for (int i = 0; i < args.Length; i++)
                    {
                        var placeholder = args[i] as StreamPlaceholder;
                        if (placeholder == null)
                        {
                            continue;
                        }

                        Log.StartingParameterStream(_logger, placeholder.StreamId);
                        var itemType = methodExecutor.MethodParameters[i].ParameterType.GetGenericArguments()[0];
                        args[i] = connection.StreamTracker.AddStream(placeholder.StreamId, itemType);
                    }
                }

                try
                {
                    InitializeHub(hub, connection);
                    Task invocation = null;

                    if (isStreamResponse)
                    {
                        var result = await ExecuteHubMethod(methodExecutor, hub, hubMethodInvocationMessage.Arguments);

                        if (!TryGetStreamingEnumerator(connection, hubMethodInvocationMessage.InvocationId, descriptor, result, out var enumerator, out var streamCts))
                        {
                            Log.InvalidReturnValueFromStreamingMethod(_logger, methodExecutor.MethodInfo.Name);
                            await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                                      $"The value returned by the streaming method '{methodExecutor.MethodInfo.Name}' is not a ChannelReader<>.");

                            return;
                        }

                        Log.StreamingResult(_logger, hubMethodInvocationMessage.InvocationId, methodExecutor);
                        _ = StreamResultsAsync(hubMethodInvocationMessage.InvocationId, connection, enumerator, scope, hubActivator, hub, streamCts);
                    }

                    else if (string.IsNullOrEmpty(hubMethodInvocationMessage.InvocationId))
                    {
                        // Send Async, no response expected
                        invocation = ExecuteHubMethod(methodExecutor, hub, hubMethodInvocationMessage.Arguments);
                    }

                    else
                    {
                        // Invoke Async, one reponse expected
                        async Task ExecuteInvocation()
                        {
                            var result = await ExecuteHubMethod(methodExecutor, hub, hubMethodInvocationMessage.Arguments);

                            Log.SendingResult(_logger, hubMethodInvocationMessage.InvocationId, methodExecutor);
                            await connection.WriteAsync(CompletionMessage.WithResult(hubMethodInvocationMessage.InvocationId, result));
                        }
                        invocation = ExecuteInvocation();
                    }

                    if (isStreamCall || isStreamResponse)
                    {
                        // don't await streaming invocations
                        // leave them running in the background, allowing dispatcher to process other messages between streaming items
                        disposeScope = false;
                    }
                    else
                    {
                        // complete the non-streaming calls now
                        await invocation;
                    }
                }
                catch (TargetInvocationException ex)
                {
                    Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex);
                    await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                              ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex.InnerException, _enableDetailedErrors));
                }
                catch (Exception ex)
                {
                    Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex);
                    await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                              ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex, _enableDetailedErrors));
                }
            }
            finally
            {
                if (disposeScope)
                {
                    hubActivator?.Release(hub);
                    scope.Dispose();
                }
            }
        }
Example #11
0
        private async Task Invoke(HubMethodDescriptor descriptor, HubConnectionContext connection,
                                  HubMethodInvocationMessage hubMethodInvocationMessage, bool isStreamedInvocation)
        {
            var methodExecutor = descriptor.MethodExecutor;

            var disposeScope = true;
            var scope        = _serviceScopeFactory.CreateScope();
            IHubActivator <THub> hubActivator = null;
            THub hub = null;

            try
            {
                if (!await IsHubMethodAuthorized(scope.ServiceProvider, connection.User, descriptor.Policies))
                {
                    Log.HubMethodNotAuthorized(_logger, hubMethodInvocationMessage.Target);
                    await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                              $"Failed to invoke '{hubMethodInvocationMessage.Target}' because user is unauthorized");

                    return;
                }

                if (!await ValidateInvocationMode(descriptor, isStreamedInvocation, hubMethodInvocationMessage, connection))
                {
                    return;
                }

                hubActivator = scope.ServiceProvider.GetRequiredService <IHubActivator <THub> >();
                hub          = hubActivator.Create();

                try
                {
                    InitializeHub(hub, connection);

                    var result = await ExecuteHubMethod(methodExecutor, hub, hubMethodInvocationMessage.Arguments);

                    if (isStreamedInvocation)
                    {
                        if (!TryGetStreamingEnumerator(connection, hubMethodInvocationMessage.InvocationId, descriptor, result, out var enumerator, out var streamCts))
                        {
                            Log.InvalidReturnValueFromStreamingMethod(_logger, methodExecutor.MethodInfo.Name);

                            await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                                      $"The value returned by the streaming method '{methodExecutor.MethodInfo.Name}' is not a ChannelReader<>.");

                            return;
                        }

                        disposeScope = false;
                        Log.StreamingResult(_logger, hubMethodInvocationMessage.InvocationId, methodExecutor);
                        // Fire-and-forget stream invocations, otherwise they would block other hub invocations from being able to run
                        _ = StreamResultsAsync(hubMethodInvocationMessage.InvocationId, connection, enumerator, scope, hubActivator, hub, streamCts);
                    }
                    // Non-empty/null InvocationId ==> Blocking invocation that needs a response
                    else if (!string.IsNullOrEmpty(hubMethodInvocationMessage.InvocationId))
                    {
                        Log.SendingResult(_logger, hubMethodInvocationMessage.InvocationId, methodExecutor);
                        await connection.WriteAsync(CompletionMessage.WithResult(hubMethodInvocationMessage.InvocationId, result));
                    }
                }
                catch (TargetInvocationException ex)
                {
                    Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex);
                    await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                              ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex.InnerException, _enableDetailedErrors));
                }
                catch (Exception ex)
                {
                    Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex);
                    await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                              ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex, _enableDetailedErrors));
                }
            }
            finally
            {
                if (disposeScope)
                {
                    hubActivator?.Release(hub);
                    scope.Dispose();
                }
            }
        }
Example #12
0
 public DefaultHubManager(IDependencyResolver resolver)
 {
     _hubProviders    = resolver.ResolveAll <IHubDescriptorProvider>();
     _methodProviders = resolver.ResolveAll <IMethodDescriptorProvider>();
     _activator       = resolver.Resolve <IHubActivator>();
 }
        private async Task StreamAsync(string invocationId, HubConnectionContext connection, object[] arguments, IServiceScope scope,
                                       IHubActivator <THub> hubActivator, THub hub, CancellationTokenSource streamCts, HubMethodInvocationMessage hubMethodInvocationMessage, HubMethodDescriptor descriptor)
        {
            string error = null;

            streamCts = streamCts ?? CancellationTokenSource.CreateLinkedTokenSource(connection.ConnectionAborted);

            try
            {
                if (!connection.ActiveRequestCancellationSources.TryAdd(invocationId, streamCts))
                {
                    Log.InvocationIdInUse(_logger, invocationId);
                    error = $"Invocation ID '{invocationId}' is already in use.";
                    return;
                }

                object result;
                try
                {
                    result = await ExecuteHubMethod(descriptor.MethodExecutor, hub, arguments, connection, scope.ServiceProvider);
                }
                catch (Exception ex)
                {
                    Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex);
                    error = ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex, _enableDetailedErrors);
                    return;
                }

                if (result == null)
                {
                    Log.InvalidReturnValueFromStreamingMethod(_logger, descriptor.MethodExecutor.MethodInfo.Name);
                    error = $"The value returned by the streaming method '{descriptor.MethodExecutor.MethodInfo.Name}' is not a ChannelReader<> or IAsyncEnumerable<>.";
                    return;
                }

                var enumerable = descriptor.FromReturnedStream(result, streamCts.Token);

                Log.StreamingResult(_logger, hubMethodInvocationMessage.InvocationId, descriptor.MethodExecutor);

                await foreach (var streamItem in enumerable)
                {
                    // Send the stream item
                    await connection.WriteAsync(new StreamItemMessage(invocationId, streamItem));
                }
            }
            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 && streamCts.IsCancellationRequested))
                {
                    error = ErrorMessageHelper.BuildErrorMessage("An error occurred on the server while streaming results.", ex, _enableDetailedErrors);
                }
            }
            finally
            {
                await CleanupInvocation(connection, hubMethodInvocationMessage, hubActivator, hub, scope);

                streamCts.Dispose();
                connection.ActiveRequestCancellationSources.TryRemove(invocationId, out _);

                await connection.WriteAsync(CompletionMessage.WithError(invocationId, error));
            }
        }
        private async Task Invoke(HubMethodDescriptor descriptor, HubConnectionContext connection,
                                  HubMethodInvocationMessage hubMethodInvocationMessage, bool isStreamResponse, bool isStreamCall)
        {
            var methodExecutor = descriptor.MethodExecutor;

            var disposeScope = true;
            var scope        = _serviceScopeFactory.CreateScope();
            IHubActivator <THub> hubActivator = null;
            THub hub = null;

            try
            {
                hubActivator = scope.ServiceProvider.GetRequiredService <IHubActivator <THub> >();
                hub          = hubActivator.Create();

                if (!await IsHubMethodAuthorized(scope.ServiceProvider, connection, descriptor, hubMethodInvocationMessage.Arguments, hub))
                {
                    Log.HubMethodNotAuthorized(_logger, hubMethodInvocationMessage.Target);
                    await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                              $"Failed to invoke '{hubMethodInvocationMessage.Target}' because user is unauthorized");

                    return;
                }

                if (!await ValidateInvocationMode(descriptor, isStreamResponse, hubMethodInvocationMessage, connection))
                {
                    return;
                }

                try
                {
                    var clientStreamLength = hubMethodInvocationMessage.StreamIds?.Length ?? 0;
                    var serverStreamLength = descriptor.StreamingParameters?.Count ?? 0;
                    if (clientStreamLength != serverStreamLength)
                    {
                        var ex = new HubException($"Client sent {clientStreamLength} stream(s), Hub method expects {serverStreamLength}.");
                        Log.InvalidHubParameters(_logger, hubMethodInvocationMessage.Target, ex);
                        await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                                  ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex, _enableDetailedErrors));

                        return;
                    }

                    InitializeHub(hub, connection);
                    Task invocation = null;

                    var arguments = hubMethodInvocationMessage.Arguments;
                    CancellationTokenSource cts = null;
                    if (descriptor.HasSyntheticArguments)
                    {
                        ReplaceArguments(descriptor, hubMethodInvocationMessage, isStreamCall, connection, ref arguments, out cts);
                    }

                    if (isStreamResponse)
                    {
                        _ = StreamAsync(hubMethodInvocationMessage.InvocationId, connection, arguments, scope, hubActivator, hub, cts, hubMethodInvocationMessage, descriptor);
                    }
                    else
                    {
                        // Invoke or Send
                        async Task ExecuteInvocation()
                        {
                            object result;

                            try
                            {
                                result = await ExecuteHubMethod(methodExecutor, hub, arguments, connection, scope.ServiceProvider);

                                Log.SendingResult(_logger, hubMethodInvocationMessage.InvocationId, methodExecutor);
                            }
                            catch (Exception ex)
                            {
                                Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex);
                                await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                                          ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex, _enableDetailedErrors));

                                return;
                            }
                            finally
                            {
                                // Stream response handles cleanup in StreamResultsAsync
                                // And normal invocations handle cleanup below in the finally
                                if (isStreamCall)
                                {
                                    await CleanupInvocation(connection, hubMethodInvocationMessage, hubActivator, hub, scope);
                                }
                            }

                            // No InvocationId - Send Async, no response expected
                            if (!string.IsNullOrEmpty(hubMethodInvocationMessage.InvocationId))
                            {
                                // Invoke Async, one reponse expected
                                await connection.WriteAsync(CompletionMessage.WithResult(hubMethodInvocationMessage.InvocationId, result));
                            }
                        }
                        invocation = ExecuteInvocation();
                    }

                    if (isStreamCall || isStreamResponse)
                    {
                        // don't await streaming invocations
                        // leave them running in the background, allowing dispatcher to process other messages between streaming items
                        disposeScope = false;
                    }
                    else
                    {
                        // complete the non-streaming calls now
                        await invocation;
                    }
                }
                catch (TargetInvocationException ex)
                {
                    Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex);
                    await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                              ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex.InnerException, _enableDetailedErrors));
                }
                catch (Exception ex)
                {
                    Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex);
                    await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                              ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex, _enableDetailedErrors));
                }
            }
            finally
            {
                if (disposeScope)
                {
                    await CleanupInvocation(connection, hubMethodInvocationMessage, hubActivator, hub, scope);
                }
            }
        }
Example #15
0
        private async Task Invoke(HubMethodDescriptor descriptor, HubConnectionContext connection,
                                  HubMethodInvocationMessage hubMethodInvocationMessage, bool isStreamResponse, bool isStreamCall)
        {
            var methodExecutor = descriptor.MethodExecutor;

            var disposeScope = true;
            var scope        = _serviceScopeFactory.CreateScope();
            IHubActivator <THub> hubActivator = null;
            THub hub = null;

            try
            {
                if (!await IsHubMethodAuthorized(scope.ServiceProvider, connection.User, descriptor.Policies))
                {
                    Log.HubMethodNotAuthorized(_logger, hubMethodInvocationMessage.Target);
                    await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                              $"Failed to invoke '{hubMethodInvocationMessage.Target}' because user is unauthorized");

                    return;
                }

                if (!await ValidateInvocationMode(descriptor, isStreamResponse, hubMethodInvocationMessage, connection))
                {
                    return;
                }

                hubActivator = scope.ServiceProvider.GetRequiredService <IHubActivator <THub> >();
                hub          = hubActivator.Create();

                if (isStreamCall)
                {
                    // swap out placeholders for channels
                    var args = hubMethodInvocationMessage.Arguments;
                    for (int i = 0; i < args.Length; i++)
                    {
                        var placeholder = args[i] as StreamPlaceholder;
                        if (placeholder == null)
                        {
                            continue;
                        }

                        Log.StartingParameterStream(_logger, placeholder.StreamId);
                        var itemType = methodExecutor.MethodParameters[i].ParameterType.GetGenericArguments()[0];
                        args[i] = connection.StreamTracker.AddStream(placeholder.StreamId, itemType);
                    }
                }

                try
                {
                    InitializeHub(hub, connection);
                    Task invocation = null;

                    CancellationTokenSource cts = null;
                    var arguments = hubMethodInvocationMessage.Arguments;
                    if (descriptor.HasSyntheticArguments)
                    {
                        // In order to add the synthetic arguments we need a new array because the invocation array is too small (it doesn't know about synthetic arguments)
                        arguments = new object[descriptor.OriginalParameterTypes.Count];

                        var hubInvocationArgumentPointer = 0;
                        for (var parameterPointer = 0; parameterPointer < arguments.Length; parameterPointer++)
                        {
                            if (hubMethodInvocationMessage.Arguments.Length > hubInvocationArgumentPointer &&
                                hubMethodInvocationMessage.Arguments[hubInvocationArgumentPointer].GetType() == descriptor.OriginalParameterTypes[parameterPointer])
                            {
                                // The types match so it isn't a synthetic argument, just copy it into the arguments array
                                arguments[parameterPointer] = hubMethodInvocationMessage.Arguments[hubInvocationArgumentPointer];
                                hubInvocationArgumentPointer++;
                            }
                            else
                            {
                                // This is the only synthetic argument type we currently support
                                if (descriptor.OriginalParameterTypes[parameterPointer] == typeof(CancellationToken))
                                {
                                    cts = CancellationTokenSource.CreateLinkedTokenSource(connection.ConnectionAborted);
                                    arguments[parameterPointer] = cts.Token;
                                }
                                else
                                {
                                    // This should never happen
                                    Debug.Assert(false, $"Failed to bind argument of type '{descriptor.OriginalParameterTypes[parameterPointer].Name}' for hub method '{methodExecutor.MethodInfo.Name}'.");
                                }
                            }
                        }
                    }

                    if (isStreamResponse)
                    {
                        var result = await ExecuteHubMethod(methodExecutor, hub, arguments);

                        if (!TryGetStreamingEnumerator(connection, hubMethodInvocationMessage.InvocationId, descriptor, result, out var enumerator, ref cts))
                        {
                            Log.InvalidReturnValueFromStreamingMethod(_logger, methodExecutor.MethodInfo.Name);
                            await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                                      $"The value returned by the streaming method '{methodExecutor.MethodInfo.Name}' is not a ChannelReader<>.");

                            return;
                        }

                        Log.StreamingResult(_logger, hubMethodInvocationMessage.InvocationId, methodExecutor);
                        _ = StreamResultsAsync(hubMethodInvocationMessage.InvocationId, connection, enumerator, scope, hubActivator, hub, cts);
                    }

                    else if (string.IsNullOrEmpty(hubMethodInvocationMessage.InvocationId))
                    {
                        // Send Async, no response expected
                        invocation = ExecuteHubMethod(methodExecutor, hub, arguments);
                    }

                    else
                    {
                        // Invoke Async, one reponse expected
                        async Task ExecuteInvocation()
                        {
                            object result;

                            try
                            {
                                result = await ExecuteHubMethod(methodExecutor, hub, arguments);

                                Log.SendingResult(_logger, hubMethodInvocationMessage.InvocationId, methodExecutor);
                            }
                            catch (Exception ex)
                            {
                                await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                                          ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex, _enableDetailedErrors));

                                return;
                            }

                            await connection.WriteAsync(CompletionMessage.WithResult(hubMethodInvocationMessage.InvocationId, result));
                        }
                        invocation = ExecuteInvocation();
                    }

                    if (isStreamCall || isStreamResponse)
                    {
                        // don't await streaming invocations
                        // leave them running in the background, allowing dispatcher to process other messages between streaming items
                        disposeScope = false;
                    }
                    else
                    {
                        // complete the non-streaming calls now
                        await invocation;
                    }
                }
                catch (TargetInvocationException ex)
                {
                    Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex);
                    await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                              ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex.InnerException, _enableDetailedErrors));
                }
                catch (Exception ex)
                {
                    Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex);
                    await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                              ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex, _enableDetailedErrors));
                }
            }
            finally
            {
                if (disposeScope)
                {
                    hubActivator?.Release(hub);
                    scope.Dispose();
                }
            }
        }
Example #16
0
        private async Task Invoke(HubMethodDescriptor descriptor, HubConnectionContext connection,
                                  HubMethodInvocationMessage hubMethodInvocationMessage, bool isStreamResponse, bool isStreamCall)
        {
            var methodExecutor = descriptor.MethodExecutor;

            var disposeScope = true;
            var scope        = _serviceScopeFactory.CreateScope();
            IHubActivator <THub> hubActivator = null;
            THub hub = null;

            try
            {
                if (!await IsHubMethodAuthorized(scope.ServiceProvider, connection, descriptor.Policies, descriptor.MethodExecutor.MethodInfo.Name, hubMethodInvocationMessage.Arguments))
                {
                    Log.HubMethodNotAuthorized(_logger, hubMethodInvocationMessage.Target);
                    await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                              $"Failed to invoke '{hubMethodInvocationMessage.Target}' because user is unauthorized");

                    return;
                }

                if (!await ValidateInvocationMode(descriptor, isStreamResponse, hubMethodInvocationMessage, connection))
                {
                    return;
                }

                hubActivator = scope.ServiceProvider.GetRequiredService <IHubActivator <THub> >();
                hub          = hubActivator.Create();

                try
                {
                    var clientStreamLength = hubMethodInvocationMessage.StreamIds?.Length ?? 0;
                    var serverStreamLength = descriptor.StreamingParameters?.Count ?? 0;
                    if (clientStreamLength != serverStreamLength)
                    {
                        var ex = new HubException($"Client sent {clientStreamLength} stream(s), Hub method expects {serverStreamLength}.");
                        Log.InvalidHubParameters(_logger, hubMethodInvocationMessage.Target, ex);
                        await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                                  ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex, _enableDetailedErrors));

                        return;
                    }

                    InitializeHub(hub, connection);
                    Task invocation = null;

                    CancellationTokenSource cts = null;
                    var arguments = hubMethodInvocationMessage.Arguments;
                    if (descriptor.HasSyntheticArguments)
                    {
                        // In order to add the synthetic arguments we need a new array because the invocation array is too small (it doesn't know about synthetic arguments)
                        arguments = new object[descriptor.OriginalParameterTypes.Count];

                        var streamPointer = 0;
                        var hubInvocationArgumentPointer = 0;
                        for (var parameterPointer = 0; parameterPointer < arguments.Length; parameterPointer++)
                        {
                            if (hubMethodInvocationMessage.Arguments.Length > hubInvocationArgumentPointer &&
                                (hubMethodInvocationMessage.Arguments[hubInvocationArgumentPointer] == null ||
                                 descriptor.OriginalParameterTypes[parameterPointer].IsAssignableFrom(hubMethodInvocationMessage.Arguments[hubInvocationArgumentPointer].GetType())))
                            {
                                // The types match so it isn't a synthetic argument, just copy it into the arguments array
                                arguments[parameterPointer] = hubMethodInvocationMessage.Arguments[hubInvocationArgumentPointer];
                                hubInvocationArgumentPointer++;
                            }
                            else
                            {
                                if (descriptor.OriginalParameterTypes[parameterPointer] == typeof(CancellationToken))
                                {
                                    cts = CancellationTokenSource.CreateLinkedTokenSource(connection.ConnectionAborted, default);
                                    arguments[parameterPointer] = cts.Token;
                                }
                                else if (isStreamCall && ReflectionHelper.IsStreamingType(descriptor.OriginalParameterTypes[parameterPointer], mustBeDirectType: true))
                                {
                                    Log.StartingParameterStream(_logger, hubMethodInvocationMessage.StreamIds[streamPointer]);
                                    var itemType = descriptor.StreamingParameters[streamPointer];
                                    arguments[parameterPointer] = connection.StreamTracker.AddStream(hubMethodInvocationMessage.StreamIds[streamPointer],
                                                                                                     itemType, descriptor.OriginalParameterTypes[parameterPointer]);

                                    streamPointer++;
                                }
                                else
                                {
                                    // This should never happen
                                    Debug.Assert(false, $"Failed to bind argument of type '{descriptor.OriginalParameterTypes[parameterPointer].Name}' for hub method '{methodExecutor.MethodInfo.Name}'.");
                                }
                            }
                        }
                    }

                    if (isStreamResponse)
                    {
                        var result = await ExecuteHubMethod(methodExecutor, hub, arguments);

                        if (result == null)
                        {
                            Log.InvalidReturnValueFromStreamingMethod(_logger, methodExecutor.MethodInfo.Name);
                            await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                                      $"The value returned by the streaming method '{methodExecutor.MethodInfo.Name}' is not a ChannelReader<> or IAsyncEnumerable<>.");

                            return;
                        }

                        cts = cts ?? CancellationTokenSource.CreateLinkedTokenSource(connection.ConnectionAborted, default);
                        connection.ActiveRequestCancellationSources.TryAdd(hubMethodInvocationMessage.InvocationId, cts);
                        var enumerable = descriptor.FromReturnedStream(result, cts.Token);

                        Log.StreamingResult(_logger, hubMethodInvocationMessage.InvocationId, methodExecutor);
                        _ = StreamResultsAsync(hubMethodInvocationMessage.InvocationId, connection, enumerable, scope, hubActivator, hub, cts, hubMethodInvocationMessage);
                    }

                    else
                    {
                        // Invoke or Send
                        async Task ExecuteInvocation()
                        {
                            object result;

                            try
                            {
                                result = await ExecuteHubMethod(methodExecutor, hub, arguments);

                                Log.SendingResult(_logger, hubMethodInvocationMessage.InvocationId, methodExecutor);
                            }
                            catch (Exception ex)
                            {
                                Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex);
                                await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                                          ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex, _enableDetailedErrors));

                                return;
                            }
                            finally
                            {
                                // Stream response handles cleanup in StreamResultsAsync
                                // And normal invocations handle cleanup below in the finally
                                if (isStreamCall)
                                {
                                    await CleanupInvocation(connection, hubMethodInvocationMessage, hubActivator, hub, scope);
                                }
                            }

                            // No InvocationId - Send Async, no response expected
                            if (!string.IsNullOrEmpty(hubMethodInvocationMessage.InvocationId))
                            {
                                // Invoke Async, one reponse expected
                                await connection.WriteAsync(CompletionMessage.WithResult(hubMethodInvocationMessage.InvocationId, result));
                            }
                        }
                        invocation = ExecuteInvocation();
                    }

                    if (isStreamCall || isStreamResponse)
                    {
                        // don't await streaming invocations
                        // leave them running in the background, allowing dispatcher to process other messages between streaming items
                        disposeScope = false;
                    }
                    else
                    {
                        // complete the non-streaming calls now
                        await invocation;
                    }
                }
                catch (TargetInvocationException ex)
                {
                    Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex);
                    await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                              ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex.InnerException, _enableDetailedErrors));
                }
                catch (Exception ex)
                {
                    Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex);
                    await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
                                              ErrorMessageHelper.BuildErrorMessage($"An unexpected error occurred invoking '{hubMethodInvocationMessage.Target}' on the server.", ex, _enableDetailedErrors));
                }
            }
            finally
            {
                if (disposeScope)
                {
                    await CleanupInvocation(connection, hubMethodInvocationMessage, hubActivator, hub, scope);
                }
            }
        }
Example #17
0
        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());
        }
Example #18
0
        private void CleanupInvocation(HubConnectionContext connection, HubMethodInvocationMessage hubMessage, IHubActivator <THub> hubActivator,
                                       THub hub, IServiceScope scope)
        {
            hubActivator?.Release(hub);
            scope.Dispose();

            if (hubMessage.StreamIds != null)
            {
                foreach (var stream in hubMessage.StreamIds)
                {
                    try
                    {
                        connection.StreamTracker.Complete(CompletionMessage.Empty(stream));
                    }
                    // ignore failures, it means the client already completed the streams
                    catch (KeyNotFoundException) { }
                }
            }
        }