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