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 void DiscoverHubMethods() { var hubType = typeof(THub); var hubTypeInfo = hubType.GetTypeInfo(); var hubName = hubType.Name; foreach (var methodInfo in HubReflectionHelper.GetHubMethods(hubType)) { if (methodInfo.IsGenericMethod) { throw new NotSupportedException($"Method '{methodInfo.Name}' is a generic method which is not supported on a Hub."); } var methodName = methodInfo.GetCustomAttribute <HubMethodNameAttribute>()?.Name ?? methodInfo.Name; if (_methods.ContainsKey(methodName)) { throw new NotSupportedException($"Duplicate definitions of '{methodName}'. Overloading is not supported."); } var executor = ObjectMethodExecutor.Create(methodInfo, hubTypeInfo); var authorizeAttributes = methodInfo.GetCustomAttributes <AuthorizeAttribute>(inherit: true); _methods[methodName] = new HubMethodDescriptor(executor, authorizeAttributes); Log.HubMethodBound(_logger, hubName, methodName); } }
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)) { Log.HubMethodNotAuthorized(_logger, 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.WriteAsync(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 void ReplaceArguments(HubMethodDescriptor descriptor, HubMethodInvocationMessage hubMethodInvocationMessage, bool isStreamCall, HubConnectionContext connection, ref object[] arguments, out CancellationTokenSource cts) { cts = null; // 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); 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 '{descriptor.MethodExecutor.MethodInfo.Name}'."); } } } }
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); } } }
private bool TryGetStreamingEnumerator(HubConnectionContext connection, string invocationId, HubMethodDescriptor hubMethodDescriptor, object result, out IAsyncEnumerator <object> enumerator) { if (result != null) { if (hubMethodDescriptor.IsObservable) { enumerator = hubMethodDescriptor.FromObservable(result, CreateCancellation()); return(true); } if (hubMethodDescriptor.IsChannel) { enumerator = hubMethodDescriptor.FromChannel(result, CreateCancellation()); return(true); } } enumerator = null; return(false); CancellationToken CreateCancellation() { var streamCts = new CancellationTokenSource(); connection.ActiveRequestCancellationSources.TryAdd(invocationId, streamCts); return(CancellationTokenSource.CreateLinkedTokenSource(connection.ConnectionAborted, streamCts.Token).Token); } }
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)) { Log.HubMethodNotAuthorized(_logger, hubMethodInvocationMessage.Target); await SendInvocationError(hubMethodInvocationMessage, connection, $"Failed to invoke '{hubMethodInvocationMessage.Target}' because user is unauthorized"); return; } if (!await ValidateInvocationMode(descriptor, isStreamedInvocation, hubMethodInvocationMessage, connection)) { return; } if (hubMethodInvocationMessage.ArgumentBindingException != null) { Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, hubMethodInvocationMessage.ArgumentBindingException); await SendInvocationError(hubMethodInvocationMessage, connection, $"Failed to invoke '{hubMethodInvocationMessage.Target}'. {hubMethodInvocationMessage.ArgumentBindingException.Message}"); 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) { if (!TryGetStreamingEnumerator(connection, hubMethodInvocationMessage.InvocationId, descriptor, result, out var enumerator)) { Log.InvalidReturnValueFromStreamingMethod(_logger, methodExecutor.MethodInfo.Name); await SendInvocationError(hubMethodInvocationMessage, connection, $"The value returned by the streaming method '{methodExecutor.MethodInfo.Name}' is null, does not implement the IObservable<> interface or is not a ReadableChannel<>."); return; } 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.WriteAsync(CompletionMessage.WithResult(hubMethodInvocationMessage.InvocationId, result)); } } catch (TargetInvocationException ex) { Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex); await SendInvocationError(hubMethodInvocationMessage, connection, BuildUnexpectedErrorMessage(hubMethodInvocationMessage.Target, ex.InnerException)); } catch (Exception ex) { Log.FailedInvokingHubMethod(_logger, hubMethodInvocationMessage.Target, ex); await SendInvocationError(hubMethodInvocationMessage, connection, BuildUnexpectedErrorMessage(hubMethodInvocationMessage.Target, ex)); } finally { hubActivator.Release(hub); } } }
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(); } } }
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(); } } }
private bool TryGetStreamingEnumerator(HubConnectionContext connection, string invocationId, HubMethodDescriptor hubMethodDescriptor, object result, out IAsyncEnumerator <object> enumerator, ref CancellationTokenSource streamCts) { if (result != null) { if (hubMethodDescriptor.IsChannel) { if (streamCts == null) { streamCts = CancellationTokenSource.CreateLinkedTokenSource(connection.ConnectionAborted); } connection.ActiveRequestCancellationSources.TryAdd(invocationId, streamCts); enumerator = hubMethodDescriptor.FromChannel(result, streamCts.Token); return(true); } } enumerator = null; return(false); }
private Task <bool> IsHubMethodAuthorized(IServiceProvider provider, HubConnectionContext hubConnectionContext, HubMethodDescriptor descriptor, object[] hubMethodArguments, Hub hub) { // If there are no policies we don't need to run auth if (descriptor.Policies.Count == 0) { return(TaskCache.True); } return(IsHubMethodAuthorizedSlow(provider, hubConnectionContext.User, descriptor.Policies, new HubInvocationContext(hubConnectionContext.HubCallerContext, provider, hub, descriptor.MethodExecutor.MethodInfo, hubMethodArguments))); }
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); } } }
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(); } } }