Beispiel #1
0
        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);
        }
Beispiel #2
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);
                                    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);
                        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);
                }
            }
        }
 public string GetUserId(HubConnectionContext connection)
 {
     return(connection.User?.Identity?.Name);
 }
Beispiel #4
0
 protected Hub()
 {
     Clients = new HubConnectionContext();
 }
Beispiel #5
0
        public Task UpdateUser(HubConnectionContext connection, UserDetailsDto userDetails)
        {
            _usersOnline.AddOrUpdate(connection, userDetails, (oldKey, oldValue) => userDetails);

            return(Task.CompletedTask);
        }
Beispiel #6
0
 public override Task OnDisconnectedAsync(HubConnectionContext connection)
 {
     return(Task.CompletedTask);
 }
Beispiel #7
0
    private async Task DispatchMessagesAsync(HubConnectionContext connection)
    {
        var input    = connection.Input;
        var protocol = connection.Protocol;

        connection.BeginClientTimeout();

        var binder = new HubConnectionBinder <THub>(_dispatcher, _lifetimeManager, connection);

        while (true)
        {
            var result = await input.ReadAsync();

            var buffer = result.Buffer;

            try
            {
                if (result.IsCanceled)
                {
                    break;
                }

                if (!buffer.IsEmpty)
                {
                    bool messageReceived = false;
                    // No message limit, just parse and dispatch
                    if (_maximumMessageSize == null)
                    {
                        while (protocol.TryParseMessage(ref buffer, binder, out var message))
                        {
                            connection.StopClientTimeout();
                            // This lets us know the timeout has stopped and we need to re-enable it after dispatching the message
                            messageReceived = true;
                            await _dispatcher.DispatchMessageAsync(connection, message);
                        }

                        if (messageReceived)
                        {
                            connection.BeginClientTimeout();
                        }
                    }
                    else
                    {
                        // We give the parser a sliding window of the default message size
                        var maxMessageSize = _maximumMessageSize.Value;

                        while (!buffer.IsEmpty)
                        {
                            var segment    = buffer;
                            var overLength = false;

                            if (segment.Length > maxMessageSize)
                            {
                                segment    = segment.Slice(segment.Start, maxMessageSize);
                                overLength = true;
                            }

                            if (protocol.TryParseMessage(ref segment, binder, out var message))
                            {
                                connection.StopClientTimeout();
                                // This lets us know the timeout has stopped and we need to re-enable it after dispatching the message
                                messageReceived = true;
                                await _dispatcher.DispatchMessageAsync(connection, message);
                            }
                            else if (overLength)
                            {
                                throw new InvalidDataException($"The maximum message size of {maxMessageSize}B was exceeded. The message size can be configured in AddHubOptions.");
                            }
                            else
                            {
                                // No need to update the buffer since we didn't parse anything
                                break;
                            }

                            // Update the buffer to the remaining segment
                            buffer = buffer.Slice(segment.Start);
                        }

                        if (messageReceived)
                        {
                            connection.BeginClientTimeout();
                        }
                    }
                }
Beispiel #8
0
 public override Task OnDisconnectedAsync(HubConnectionContext connection)
 {
     throw new NotSupportedException();
 }
 /// <summary>
 /// Returns the User ID for the given <see cref="HubConnectionContext"/>.
 /// </summary>
 /// <param name="connection">The <see cref="HubConnectionContext"/> of the user to locate.</param>
 /// <returns>The value of the <see cref="UserIdClaimName"/> claim.</returns>
 public virtual string GetUserId(HubConnectionContext connection) =>
 GetUserId(connection.User);
Beispiel #10
0
 public string GetUserId(HubConnectionContext connection)
 {
     return(connection.User?.Claims?
            .Where(x => x.Type == nameof(User.Id))
            .Select(x => x.Value).FirstOrDefault());
 }
Beispiel #11
0
 public override Task OnDisconnectedAsync(HubConnectionContext connection)
 {
     throw new System.NotImplementedException();
 }
Beispiel #12
0
 public string GetUserId(HubConnectionContext connection)
 {
     throw new System.NotImplementedException();
 }
 public string GetUserId(HubConnectionContext connection)
 {
     return("UserId!");
 }
 public virtual string GetUserId(HubConnectionContext connection) =>
 connection.User?.FindFirst(ClaimTypes.Name)?.Value;
Beispiel #15
0
 private Task SendLocal(HubConnectionContext connection, HubInvocationMessage hubMessage)
 {
     _logger.LogDebug("Sending local message to connection {connectionId} on hub {hubName} (serverId: {serverId})",
                      connection.ConnectionId, _hubName, _serverId);
     return(connection.WriteAsync(hubMessage).AsTask());
 }
Beispiel #16
0
 public string GetUserId(HubConnectionContext connection)
 {
     return(connection.User?.FindFirst("sub")?.Value);
 }
 public string GetUserId(HubConnectionContext connection) => connection.GetHttpContext().Request.Query["token"];
Beispiel #18
0
        public Task <UserDetailsDto> GetUser(HubConnectionContext connection)
        {
            var user = _usersOnline.Values.FirstOrDefault(u => u.ConnectionId == connection.ConnectionId);

            return(Task.FromResult(user));
        }
 public virtual string GetUserId(HubConnectionContext connection)
 {
     return(connection.User?.Claims.FirstOrDefault(x => x.Value != null).Value);
 }
Beispiel #20
0
 /// <summary>
 /// GetUserId
 /// </summary>
 /// <param name="connection"></param>
 /// <returns></returns>
 public string GetUserId(HubConnectionContext connection)
 {
     return(connection.User?.FindFirst(ClaimTypes.Name)?.Value);
 }
Beispiel #21
0
 public string?GetUserId(HubConnectionContext connection)
 {
     return(connection.User.GetClaim("id")?.Value);
 }
 string IUserIdProvider.GetUserId(HubConnectionContext connection)
 {
     return(connection.User?.Claims?.FirstOrDefault(c => c.Type == JwtCustomClaimNames.Id)?.Value);
 }
Beispiel #23
0
 public virtual string GetUserId(HubConnectionContext connection)
 {
     return(connection.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value);
 }
Beispiel #24
0
 private Task ProcessStreamItem(HubConnectionContext connection, StreamItemMessage message)
 {
     Log.ReceivedStreamItem(_logger, message);
     return(connection.StreamTracker.ProcessItem(message));
 }
Beispiel #25
0
 public virtual string GetUserId(HubConnectionContext connection)
 {
     return(connection.User.FindFirstValue(AppConstants.SubjectClaimName));
 }
Beispiel #26
0
 private void InitializeHub(THub hub, HubConnectionContext connection)
 {
     hub.Clients = new HubCallerClients(_hubContext.Clients, connection.ConnectionId);
     hub.Context = connection.HubCallerContext;
     hub.Groups  = _hubContext.Groups;
 }
 public string GetUserId(HubConnectionContext connection)
 {
     return(connection.User?.FindFirst(claim => claim.Type == "username")?.Value);
 }
Beispiel #28
0
        public override Task DispatchMessageAsync(HubConnectionContext connection, HubMessage hubMessage)
        {
            // Messages are dispatched sequentially and will stop other messages from being processed until they complete.
            // Streaming methods will run sequentially until they start streaming, then they will fire-and-forget allowing other messages to run.

            switch (hubMessage)
            {
            case InvocationBindingFailureMessage bindingFailureMessage:
                return(ProcessInvocationBindingFailure(connection, bindingFailureMessage));

            case StreamBindingFailureMessage bindingFailureMessage:
                return(ProcessStreamBindingFailure(connection, bindingFailureMessage));

            case InvocationMessage invocationMessage:
                Log.ReceivedHubInvocation(_logger, invocationMessage);
                return(ProcessInvocation(connection, invocationMessage, isStreamResponse: false));

            case StreamInvocationMessage streamInvocationMessage:
                Log.ReceivedStreamHubInvocation(_logger, streamInvocationMessage);
                return(ProcessInvocation(connection, streamInvocationMessage, isStreamResponse: true));

            case CancelInvocationMessage cancelInvocationMessage:
                // Check if there is an associated active stream and cancel it if it exists.
                // The cts will be removed when the streaming method completes executing
                if (connection.ActiveRequestCancellationSources.TryGetValue(cancelInvocationMessage.InvocationId, out var cts))
                {
                    Log.CancelStream(_logger, cancelInvocationMessage.InvocationId);
                    cts.Cancel();
                }
                else
                {
                    // Stream can be canceled on the server while client is canceling stream.
                    Log.UnexpectedCancel(_logger);
                }
                break;

            case PingMessage _:
                connection.StartClientTimeout();
                break;

            case StreamItemMessage streamItem:
                return(ProcessStreamItem(connection, streamItem));

            case CompletionMessage streamCompleteMessage:
                // closes channels, removes from Lookup dict
                // user's method can see the channel is complete and begin wrapping up
                if (connection.StreamTracker.TryComplete(streamCompleteMessage))
                {
                    Log.CompletingStream(_logger, streamCompleteMessage);
                }
                else
                {
                    Log.UnexpectedStreamCompletion(_logger);
                }
                break;

            // Other kind of message we weren't expecting
            default:
                Log.UnsupportedMessageReceived(_logger, hubMessage.GetType().FullName);
                throw new NotSupportedException($"Received unsupported message: {hubMessage}");
            }

            return(Task.CompletedTask);
        }
        public string GetUserId(HubConnectionContext hubConnection)
        {
            string userId = "dummyUser";

            return(userId);
        }
 protected Hub()
 {
     Clients = new HubConnectionContext();
 }
 public string GetUserId(HubConnectionContext connection) => connection.User?.FindFirstValue("sub");