Line data Source code
1 : #region Copyright
2 : // // -----------------------------------------------------------------------
3 : // // <copyright company="cdmdotnet Limited">
4 : // // Copyright cdmdotnet Limited. All rights reserved.
5 : // // </copyright>
6 : // // -----------------------------------------------------------------------
7 : #endregion
8 :
9 : using System;
10 : using System.Collections.Generic;
11 : using System.Linq;
12 : using System.Threading;
13 : using System.Threading.Tasks;
14 : using cdmdotnet.Logging;
15 : using Cqrs.Events;
16 : using Microsoft.AspNet.SignalR;
17 :
18 : namespace Cqrs.WebApi.SignalR.Hubs
19 : {
20 : public class NotificationHub
21 : : Hub
22 : , INotificationHub
23 : , ISingleSignOnTokenNotificationHub
24 0 : {
25 0 : public NotificationHub(ILogger logger, ICorrelationIdHelper correlationIdHelper)
26 : {
27 : Logger = logger;
28 : CorrelationIdHelper = correlationIdHelper;
29 : }
30 :
31 0 : public NotificationHub()
32 : {
33 : }
34 :
35 : public ILogger Logger { get; set; }
36 :
37 : public ICorrelationIdHelper CorrelationIdHelper { get; set; }
38 :
39 : public Func<string, Guid> ConvertUserTokenToUserRsn { get; set; }
40 :
41 : #region Overrides of HubBase
42 :
43 : /// <summary>
44 : /// When the connection connects to this hub instance we register the connection so we can respond back to it.
45 : /// </summary>
46 1 : public override Task OnConnected()
47 : {
48 : return Join();
49 : }
50 :
51 : /// <summary>
52 : /// When the connection reconnects to this hub instance we register the connection so we can respond back to it.
53 : /// </summary>
54 1 : public override Task OnReconnected()
55 : {
56 : return Join();
57 : }
58 :
59 : #endregion
60 :
61 0 : protected virtual string UserToken()
62 : {
63 : string userRsn;
64 : Cookie cookie;
65 : if (Context.RequestCookies.TryGetValue("X-Token", out cookie))
66 : userRsn = cookie.Value;
67 : else
68 : userRsn = Context.QueryString["X-Token"];
69 :
70 : return userRsn.Replace(".", string.Empty);
71 : }
72 :
73 0 : protected virtual Task Join()
74 : {
75 : string userToken = UserToken();
76 : string connectionId = Context.ConnectionId;
77 : return Task.Factory.StartNewSafely(() =>
78 : {
79 : Task work = Groups.Add(connectionId, string.Format("User-{0}", userToken));
80 : work.ConfigureAwait(false);
81 : work.Wait();
82 :
83 : CurrentHub
84 : .Clients
85 : .Group(string.Format("User-{0}", userToken))
86 : .registered("User: " + userToken);
87 :
88 : if (ConvertUserTokenToUserRsn != null)
89 : {
90 : try
91 : {
92 : Guid userRsn = ConvertUserTokenToUserRsn(userToken);
93 : work = Groups.Add(connectionId, string.Format("UserRsn-{0}", userRsn));
94 : work.ConfigureAwait(false);
95 : work.Wait();
96 :
97 : CurrentHub
98 : .Clients
99 : .Group(string.Format("UserRsn-{0}", userRsn))
100 : .registered("UserRsn: " + userRsn);
101 :
102 : }
103 : catch (Exception exception)
104 : {
105 : Logger.LogWarning(string.Format("Registering user token '{0}' to a user RSN and into the SignalR group failed.", userToken), exception: exception, metaData: GetAdditionalDataForLogging(userToken));
106 : }
107 : }
108 : });
109 : }
110 :
111 : protected virtual IHubContext CurrentHub
112 : {
113 : get
114 : {
115 : return GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
116 : }
117 : }
118 :
119 : /// <summary>
120 : /// Send out an event to specific user RSNs
121 : /// </summary>
122 : void INotificationHub.SendUsersEvent<TSingleSignOnToken>(IEvent<TSingleSignOnToken> eventData, params Guid[] userRsnCollection)
123 : {
124 : IList<Guid> optimisedUserRsnCollection = (userRsnCollection ?? Enumerable.Empty<Guid>()).ToList();
125 :
126 : Logger.LogDebug(string.Format("Sending a message on the hub for user RSNs [{0}].", string.Join(", ", optimisedUserRsnCollection)));
127 :
128 : try
129 : {
130 : var tokenSource = new CancellationTokenSource();
131 : Task.Factory.StartNewSafely
132 : (
133 : () =>
134 : {
135 : foreach (Guid userRsn in optimisedUserRsnCollection)
136 : {
137 : var metaData = GetAdditionalDataForLogging(userRsn);
138 : try
139 : {
140 : Clients
141 : .Group(string.Format("UserRsn-{0}", userRsn))
142 : .notifyEvent(eventData);
143 : }
144 : catch (TimeoutException exception)
145 : {
146 : Logger.LogWarning("Sending a message on the hub timed-out.", exception: exception, metaData: metaData);
147 : }
148 : catch (Exception exception)
149 : {
150 : Logger.LogError("Sending a message on the hub resulted in an error.", exception: exception, metaData: metaData);
151 : }
152 : }
153 : }, tokenSource.Token
154 : );
155 :
156 : tokenSource.CancelAfter(15 * 1000);
157 : }
158 : catch (Exception exception)
159 : {
160 : foreach (Guid userRsn in optimisedUserRsnCollection)
161 : Logger.LogError("Queueing a message on the hub resulted in an error.", exception: exception, metaData: GetAdditionalDataForLogging(userRsn));
162 : }
163 : }
164 :
165 : /// <summary>
166 : /// Send out an event to specific user token
167 : /// </summary>
168 : void INotificationHub.SendUserEvent<TSingleSignOnToken>(IEvent<TSingleSignOnToken> eventData, string userToken)
169 : {
170 : Logger.LogDebug(string.Format("Sending a message on the hub for user [{0}].", userToken));
171 :
172 : try
173 : {
174 : var tokenSource = new CancellationTokenSource();
175 : Task.Factory.StartNewSafely
176 : (
177 : () =>
178 : {
179 : var metaData = GetAdditionalDataForLogging(userToken);
180 :
181 : try
182 : {
183 : CurrentHub
184 : .Clients
185 : .Group(string.Format("User-{0}", userToken))
186 : .notifyEvent(new { Type = eventData.GetType().FullName, Data = eventData });
187 : }
188 : catch (TimeoutException exception)
189 : {
190 : Logger.LogWarning("Sending a message on the hub timed-out.", exception: exception, metaData: metaData);
191 : }
192 : catch (Exception exception)
193 : {
194 : Logger.LogError("Sending a message on the hub resulted in an error.", exception: exception, metaData: metaData);
195 : }
196 : }, tokenSource.Token
197 : );
198 :
199 : tokenSource.CancelAfter(15 * 1000);
200 : }
201 : catch (Exception exception)
202 : {
203 : Logger.LogError("Queueing a message on the hub resulted in an error.", exception: exception, metaData: GetAdditionalDataForLogging(userToken));
204 : }
205 : }
206 :
207 : /// <summary>
208 : /// Send out an event to all users
209 : /// </summary>
210 : void INotificationHub.SendAllUsersEvent<TSingleSignOnToken>(IEvent<TSingleSignOnToken> eventData)
211 : {
212 : Logger.LogDebug("Sending a message on the hub to all users.");
213 :
214 : try
215 : {
216 : var tokenSource = new CancellationTokenSource();
217 : Task.Factory.StartNewSafely
218 : (
219 : () =>
220 : {
221 : try
222 : {
223 : CurrentHub
224 : .Clients
225 : .All
226 : .notifyEvent(new { Type = eventData.GetType().FullName, Data = eventData });
227 : }
228 : catch (TimeoutException exception)
229 : {
230 : Logger.LogWarning("Sending a message on the hub to all users timed-out.", exception: exception);
231 : }
232 : catch (Exception exception)
233 : {
234 : Logger.LogError("Sending a message on the hub to all users resulted in an error.", exception: exception);
235 : }
236 : }, tokenSource.Token
237 : );
238 :
239 : tokenSource.CancelAfter(15 * 1000);
240 : }
241 : catch (Exception exception)
242 : {
243 : Logger.LogError("Queueing a message on the hub to all users resulted in an error.", exception: exception);
244 : }
245 : }
246 :
247 : /// <summary>
248 : /// Send out an event to all users except the specific user token
249 : /// </summary>
250 : void INotificationHub.SendExceptThisUserEvent<TSingleSignOnToken>(IEvent<TSingleSignOnToken> eventData, string userToken)
251 : {
252 : Logger.LogDebug(string.Format("Sending a message on the hub for all users except user [{0}].", userToken));
253 :
254 : return;
255 :
256 : try
257 : {
258 : var tokenSource = new CancellationTokenSource();
259 : Task.Factory.StartNewSafely
260 : (
261 : () =>
262 : {
263 : var metaData = GetAdditionalDataForLogging(userToken);
264 :
265 : try
266 : {
267 : CurrentHub
268 : .Clients
269 : .Group(string.Format("User-{0}", userToken))
270 : .notifyEvent(new { Type = eventData.GetType().FullName, Data = eventData });
271 : }
272 : catch (TimeoutException exception)
273 : {
274 : Logger.LogWarning(string.Format("Sending a message on the hub for all users except user [{0}] timed-out.", userToken), exception: exception, metaData: metaData);
275 : }
276 : catch (Exception exception)
277 : {
278 : Logger.LogError(string.Format("Sending a message on the hub for all users except user [{0}] resulted in an error.", userToken), exception: exception, metaData: metaData);
279 : }
280 : }, tokenSource.Token
281 : );
282 :
283 : tokenSource.CancelAfter(15 * 1000);
284 : }
285 : catch (Exception exception)
286 : {
287 : Logger.LogError("Queueing a message on the hub resulted in an error.", exception: exception, metaData: GetAdditionalDataForLogging(userToken));
288 : }
289 : }
290 :
291 : /// <summary>
292 : /// Send out an event to specific user token
293 : /// </summary>
294 : void ISingleSignOnTokenNotificationHub.SendUserEvent<TSingleSignOnToken>(IEvent<TSingleSignOnToken> eventData, string userToken)
295 : {
296 : ((INotificationHub) this).SendUserEvent(eventData, userToken);
297 : }
298 :
299 : /// <summary>
300 : /// Send out an event to all users
301 : /// </summary>
302 : void ISingleSignOnTokenNotificationHub.SendAllUsersEvent<TSingleSignOnToken>(IEvent<TSingleSignOnToken> eventData)
303 : {
304 : ((INotificationHub)this).SendAllUsersEvent(eventData);
305 : }
306 :
307 : /// <summary>
308 : /// Send out an event to all users except the specific user token
309 : /// </summary>
310 : void ISingleSignOnTokenNotificationHub.SendExceptThisUserEvent<TSingleSignOnToken>(IEvent<TSingleSignOnToken> eventData, string userToken)
311 : {
312 : ((INotificationHub)this).SendExceptThisUserEvent(eventData, userToken);
313 : }
314 :
315 : /// <summary>
316 : /// Send out an event to specific user RSNs
317 : /// </summary>
318 : void ISingleSignOnTokenNotificationHub.SendUsersEvent<TSingleSignOnToken>(IEvent<TSingleSignOnToken> eventData, params Guid[] userRsnCollection)
319 : {
320 : ((INotificationHub)this).SendUsersEvent(eventData, userRsnCollection);
321 : }
322 :
323 0 : protected virtual IDictionary<string, object> GetAdditionalDataForLogging(Guid userRsn)
324 : {
325 : return new Dictionary<string, object> { { "UserRsn", userRsn } };
326 : }
327 :
328 0 : protected virtual IDictionary<string, object> GetAdditionalDataForLogging(string userToken)
329 : {
330 : return new Dictionary<string, object> { { "UserToken", userToken } };
331 : }
332 : }
333 : }
|