private void InitializeHub(THub hub, HubConnectionContext connection) { hub.Clients = _hubContext.Clients; hub.Context = new HubCallerContext(connection); hub.Groups = _hubContext.Groups; }
public async Task OnConnectedAsync(ConnectionContext connection) { var output = Channel.CreateUnbounded <HubMessage>(); // Set the hub feature before doing anything else. This stores // all the relevant state for a SignalR Hub connection. connection.Features.Set <IHubFeature>(new HubFeature()); var connectionContext = new HubConnectionContext(output, connection); if (!await ProcessNegotiate(connectionContext)) { return; } // Hubs support multiple producers so we set up this loop to copy // data written to the HubConnectionContext's channel to the transport channel var protocolReaderWriter = connectionContext.ProtocolReaderWriter; async Task WriteToTransport() { try { while (await output.In.WaitToReadAsync()) { while (output.In.TryRead(out var hubMessage)) { var buffer = protocolReaderWriter.WriteMessage(hubMessage); while (await connection.Transport.Out.WaitToWriteAsync()) { if (connection.Transport.Out.TryWrite(buffer)) { break; } } } } } catch (Exception ex) { connectionContext.Abort(ex); } } var writingOutputTask = WriteToTransport(); try { await _lifetimeManager.OnConnectedAsync(connectionContext); await RunHubAsync(connectionContext); } finally { await _lifetimeManager.OnDisconnectedAsync(connectionContext); // Nothing should be writing to the HubConnectionContext output.Out.TryComplete(); // This should unwind once we complete the output await writingOutputTask; } }
private async Task DispatchMessagesAsync(HubConnectionContext connection) { var input = connection.Input; var protocol = connection.Protocol; connection.BeginClientTimeout(); var binder = new HubConnectionBinder <THub>(_dispatcher, connection); while (true) { var result = await input.ReadAsync(); var buffer = result.Buffer; connection.ResetClientTimeout(); 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)) { messageReceived = true; connection.StopClientTimeout(); 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)) { messageReceived = true; connection.StopClientTimeout(); 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(); } } }
private async Task Invoke(HubMethodDescriptor descriptor, HubConnectionContext connection, InvocationMessage invocationMessage) { var methodExecutor = descriptor.MethodExecutor; using (var scope = _serviceScopeFactory.CreateScope()) { if (!await IsHubMethodAuthorized(scope.ServiceProvider, connection.User, descriptor.Policies)) { _logger.HubMethodNotAuthorized(invocationMessage.Target); if (!invocationMessage.NonBlocking) { await SendMessageAsync(connection, CompletionMessage.WithError(invocationMessage.InvocationId, $"Failed to invoke '{invocationMessage.Target}' because user is unauthorized")); } return; } var hubActivator = scope.ServiceProvider.GetRequiredService <IHubActivator <THub> >(); var hub = hubActivator.Create(); try { InitializeHub(hub, connection); object result = null; // ReadableChannel is awaitable but we don't want to await it. if (methodExecutor.IsMethodAsync && !IsChannel(methodExecutor.MethodReturnType, out _)) { if (methodExecutor.MethodReturnType == typeof(Task)) { await(Task) methodExecutor.Execute(hub, invocationMessage.Arguments); } else { result = await methodExecutor.ExecuteAsync(hub, invocationMessage.Arguments); } } else { result = methodExecutor.Execute(hub, invocationMessage.Arguments); } if (IsStreamed(connection, methodExecutor, result, methodExecutor.MethodReturnType, out var enumerator)) { _logger.StreamingResult(invocationMessage.InvocationId, methodExecutor.MethodReturnType.FullName); await StreamResultsAsync(invocationMessage.InvocationId, connection, enumerator); } else if (!invocationMessage.NonBlocking) { _logger.SendingResult(invocationMessage.InvocationId, methodExecutor.MethodReturnType.FullName); await SendMessageAsync(connection, CompletionMessage.WithResult(invocationMessage.InvocationId, result)); } } catch (TargetInvocationException ex) { _logger.FailedInvokingHubMethod(invocationMessage.Target, ex); if (!invocationMessage.NonBlocking) { await SendMessageAsync(connection, CompletionMessage.WithError(invocationMessage.InvocationId, ex.InnerException.Message)); } } catch (Exception ex) { _logger.FailedInvokingHubMethod(invocationMessage.Target, ex); if (!invocationMessage.NonBlocking) { await SendMessageAsync(connection, CompletionMessage.WithError(invocationMessage.InvocationId, ex.Message)); } } finally { hubActivator.Release(hub); } } }
public override Task OnConnectedAsync(HubConnectionContext connection) { _connections.Add(connection); return(Task.CompletedTask); }
public static void AddUid(this HubConnectionContext connection, string uid) { connection.Metadata.Add("UID", uid); }
private Task SendMessageAsync(HubConnectionContext connection, HubMessage hubMessage) { return(connection.WriteAsync(hubMessage)); }
public void Remove(HubConnectionContext connection) { _connections.TryRemove(connection.ConnectionId, out _); }
protected Hub() { Clients = new HubConnectionContext(); }
private async Task DispatchMessagesAsync(HubConnectionContext connection) { // Since we dispatch multiple hub invocations in parallel, we need a way to communicate failure back to the main processing loop. // This is done by aborting the connection. try { while (true) { var result = await connection.Input.ReadAsync(connection.ConnectionAbortedToken); var buffer = result.Buffer; var consumed = buffer.End; var examined = buffer.End; try { if (!buffer.IsEmpty) { if (connection.ProtocolReaderWriter.ReadMessages(buffer, this, out var hubMessages, out consumed, out examined)) { foreach (var hubMessage in hubMessages) { switch (hubMessage) { case InvocationMessage invocationMessage: _logger.ReceivedHubInvocation(invocationMessage); // Don't wait on the result of execution, continue processing other // incoming messages on this connection. _ = ProcessInvocation(connection, invocationMessage, isStreamedInvocation: false); break; case StreamInvocationMessage streamInvocationMessage: _logger.ReceivedStreamHubInvocation(streamInvocationMessage); // Don't wait on the result of execution, continue processing other // incoming messages on this connection. _ = ProcessInvocation(connection, streamInvocationMessage, isStreamedInvocation: true); break; 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)) { _logger.CancelStream(cancelInvocationMessage.InvocationId); cts.Cancel(); } else { // Stream can be canceled on the server while client is canceling stream. _logger.UnexpectedCancel(); } break; case PingMessage _: // We don't care about pings break; // Other kind of message we weren't expecting default: _logger.UnsupportedMessageReceived(hubMessage.GetType().FullName); throw new NotSupportedException($"Received unsupported message: {hubMessage}"); } } } } else if (result.IsCompleted) { break; } } finally { connection.Input.AdvanceTo(consumed, examined); } } } catch (OperationCanceledException) { // If there's an exception, bubble it to the caller connection.AbortException?.Throw(); } }
public async Task OnCompletionAsync(HubConnectionContext connection, CompletionMessage message) { var hubName = GetHubName(connection); await _hubMessageBroker.PassThruServerMessage(hubName, connection, message); }
public HubCallerContext(HubConnectionContext connection) { Connection = connection; }
public static string GetHubName(this HubConnectionContext connection) { return(connection.GetHttpContext()?.Request.Query["hub"]); }
public async Task OnCompletionAsync(HubConnectionContext connection, CompletionMessage message) { await Task.CompletedTask; }
private async Task <bool> ValidateInvocationMode(Type resultType, bool isStreamedInvocation, HubMethodInvocationMessage hubMethodInvocationMessage, HubConnectionContext connection) { var isStreamedResult = IsStreamed(resultType); if (isStreamedResult && !isStreamedInvocation) { // Non-null/empty InvocationId? Blocking if (!string.IsNullOrEmpty(hubMethodInvocationMessage.InvocationId)) { _logger.StreamingMethodCalledWithInvoke(hubMethodInvocationMessage); await SendMessageAsync(connection, CompletionMessage.WithError(hubMethodInvocationMessage.InvocationId, $"The client attempted to invoke the streaming '{hubMethodInvocationMessage.Target}' method in a non-streaming fashion.")); } return(false); } if (!isStreamedResult && isStreamedInvocation) { _logger.NonStreamingMethodCalledWithStream(hubMethodInvocationMessage); await SendMessageAsync(connection, CompletionMessage.WithError(hubMethodInvocationMessage.InvocationId, $"The client attempted to invoke the non-streaming '{hubMethodInvocationMessage.Target}' method in a streaming fashion.")); return(false); } return(true); }
public void Add(HubConnectionContext connection) { _connections.TryAdd(connection.ConnectionId, connection); }
public static HttpContext GetHttpContext(this HubConnectionContext connection) { return(connection.Features.Get <IHttpContextFeature>()?.HttpContext); }
/// <inheritdoc /> public virtual string? GetUserId(HubConnectionContext connection) { return connection.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; }
public abstract Task OnDisconnectedAsync(HubConnectionContext connection);
public override Task OnDisconnectedAsync(HubConnectionContext connection) { _connections.Remove(connection); _groups.RemoveDisconnectedConnection(connection.ConnectionId); return(Task.CompletedTask); }
public static void AddRouteTarget(this HubConnectionContext connection, RouteTarget target) { connection.Metadata.Add("Target", target); }