tgstation-server 6.12.0
The /tg/station 13 server suite
Loading...
Searching...
No Matches
ComprehensiveHubContext.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Concurrent;
3using System.Collections.Generic;
4using System.Linq;
5using System.Threading;
6using System.Threading.Tasks;
7
8using Microsoft.AspNetCore.SignalR;
9using Microsoft.Extensions.Logging;
10
13
15{
21 sealed class ComprehensiveHubContext<THub, THubMethods> : IConnectionMappedHubContext<THub, THubMethods>, IHubConnectionMapper<THub, THubMethods>
22 where THub : ConnectionMappingHub<THub, THubMethods>
23 where THubMethods : class
24 {
26 public IHubClients<THubMethods> Clients => wrappedHubContext.Clients;
27
29 public IGroupManager Groups => wrappedHubContext.Groups;
30
35
39 readonly ILogger<ComprehensiveHubContext<THub, THubMethods>> logger;
40
44 readonly ConcurrentDictionary<long, Dictionary<string, HubCallerContext>> userConnections;
45
47 public event Func<IAuthenticationContext, Func<IEnumerable<string>, Task>, CancellationToken, ValueTask>? OnConnectionMapGroups;
48
57 {
58 this.wrappedHubContext = wrappedHubContext ?? throw new ArgumentNullException(nameof(wrappedHubContext));
59 this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
60
61 userConnections = new ConcurrentDictionary<long, Dictionary<string, HubCallerContext>>();
62 }
63
65 public List<string> UserConnectionIds(User user)
66 {
67 ArgumentNullException.ThrowIfNull(user);
68 var connectionIds = userConnections.GetOrAdd(user.Require(x => x.Id), _ => new Dictionary<string, HubCallerContext>());
69 lock (connectionIds)
70 return connectionIds.Keys.ToList();
71 }
72
74 public ValueTask UserConnected(IAuthenticationContext authenticationContext, THub hub, CancellationToken cancellationToken)
75 {
76 ArgumentNullException.ThrowIfNull(authenticationContext);
77 ArgumentNullException.ThrowIfNull(hub);
78
79 var userId = authenticationContext.User.Require(x => x.Id);
80 var context = hub.Context;
81 logger.LogTrace(
82 "Mapping user {userId} to hub connection ID: {connectionId}",
83 userId,
84 context.ConnectionId);
85
86 var mappingTask = OnConnectionMapGroups?.Invoke(
87 authenticationContext,
88 mappedGroups =>
89 {
90 mappedGroups = mappedGroups.ToList();
91 logger.LogTrace(
92 "Mapping connection ID {connectionId} with groups: {mappedGroups}",
93 context.ConnectionId,
94 String.Join(", ", mappedGroups));
95 return Task.WhenAll(
96 mappedGroups.Select(
97 group => hub.Groups.AddToGroupAsync(context.ConnectionId, group, cancellationToken)));
98 },
99 cancellationToken)
100 ?? ValueTask.CompletedTask;
101 userConnections.AddOrUpdate(
102 userId,
103 _ => new Dictionary<string, HubCallerContext>
104 {
105 { context.ConnectionId, context },
106 },
107 (_, old) =>
108 {
109 lock (old)
110 old[context.ConnectionId] = context;
111
112 return old;
113 });
114
115 return mappingTask;
116 }
117
119 public void UserDisconnected(string connectionId)
120 {
121 ArgumentNullException.ThrowIfNull(connectionId);
122 foreach (var kvp in userConnections)
123 lock (kvp.Value)
124 if (kvp.Value.Remove(connectionId))
125 logger.LogTrace("User {userId} disconnected connection ID: {connectionId}", kvp.Key, connectionId);
126 }
127
130 {
131 ArgumentNullException.ThrowIfNull(user);
132 var uid = user.Require(x => x.Id);
133 logger.LogTrace("NotifyAndAbortUnauthedConnections. UID {userId}", uid);
134
135 List<HubCallerContext>? connections = null;
136 userConnections.AddOrUpdate(
137 uid,
138 _ => new Dictionary<string, HubCallerContext>(),
139 (_, old) =>
140 {
141 lock (old)
142 {
143 connections = old.Values.ToList();
144 old.Clear();
145 }
146
147 return old;
148 });
149
150 if (connections != null)
151 foreach (var context in connections)
152 context.Abort();
153 }
154 }
155}
An implementation of IHubContext<THub> with User connection ID mapping.
readonly ILogger< ComprehensiveHubContext< THub, THubMethods > > logger
The ILogger for the ComprehensiveHubContext<THub, THubMethods>.
ValueTask UserConnected(IAuthenticationContext authenticationContext, THub hub, CancellationToken cancellationToken)
To be called when a hub connection is made.A ValueTask representing the running operation.
readonly IHubContext< THub, THubMethods > wrappedHubContext
The IHubContext<THub> being wrapped.
ComprehensiveHubContext(IHubContext< THub, THubMethods > wrappedHubContext, ILogger< ComprehensiveHubContext< THub, THubMethods > > logger)
Initializes a new instance of the ComprehensiveHubContext<THub, THubMethods> class.
readonly ConcurrentDictionary< long, Dictionary< string, HubCallerContext > > userConnections
Map of User Api.Models.EntityId.Ids to their associated HubCallerContexts.
List< string > UserConnectionIds(User user)
Gets a List<T> of current connection IDs for a given user .A List<T> representing the active connecti...
Func< IAuthenticationContext, Func< IEnumerable< string >, Task >, CancellationToken, ValueTask >? OnConnectionMapGroups
void AbortUnauthedConnections(User user)
Aborts the connections associated with the given user .
void UserDisconnected(string connectionId)
To be called when a hub connection is terminated.
Base class for Hub<T>s that want to map their connection IDs to Models.PermissionSets.
For creating and accessing authentication contexts.
A IHubContext<THub> that maps Users to their connection IDs.
Handles mapping connection IDs to Users for a given THub .