Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ClientEvents.cs
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Threading.Tasks;
5using Waher.Content;
8using Waher.Events;
14using Waher.Script;
16using Waher.Security;
17
18namespace Waher.IoTGateway
19{
51 {
55 public const int TabIdCacheTimeoutSeconds = 30;
56
61
65 public ClientEvents()
66 : base("/ClientEvents")
67 {
68 }
69
73 public override bool HandlesSubPaths => true;
74
78 public override bool UserSessions => true;
79
83 public bool AllowsGET => true;
84
88 public bool AllowsPOST => true;
89
96 public async Task GET(HttpRequest Request, HttpResponse Response)
97 {
98 if (string.IsNullOrEmpty(Request.SubPath))
99 throw new BadRequestException("Sub-path missing.");
100
101 string Id = Request.SubPath.Substring(1);
102 string ContentType;
103 byte[] Content;
104 bool More;
105
106 lock (requestsByContentID)
107 {
108 if (requestsByContentID.TryGetValue(Id, out ContentQueue Queue))
109 {
110 if (Queue.Content is null)
111 {
112 Queue.Response = Response;
113 return;
114 }
115
116 More = Queue.More;
117 Content = Queue.Content;
118 ContentType = Queue.ContentType;
119
120 if (More)
121 {
122 Queue.More = false;
123 Queue.Content = null;
124 Queue.ContentType = null;
125 }
126 else
127 requestsByContentID.Remove(Id);
128 }
129 else
130 {
131 requestsByContentID[Id] = new ContentQueue(Id)
132 {
133 Response = Response
134 };
135
136 return;
137 }
138 }
139
140 SetTransparentCorsHeaders(this, Request, Response);
141
142 Response.SetHeader("X-More", More ? "1" : "0");
143 Response.ContentType = ContentType;
144 await Response.Write(Content, 0, Content.Length);
145 await Response.SendResponse();
146 }
147
154 public async Task POST(HttpRequest Request, HttpResponse Response)
155 {
156 if (!Request.HasData || Request.Session is null)
157 throw new BadRequestException("POST request missing data.");
158
159 // TODO: Check User authenticated
160
161 object Obj = await Request.DecodeDataAsync();
162 if (!(Obj is string Location))
163 throw new BadRequestException("Expected location.");
164
165 string TabID = Request.Header["X-TabID"];
166 if (string.IsNullOrEmpty(TabID))
167 throw new BadRequestException("Expected X-TabID header.");
168
169 TabQueue Queue = Register(Request, Response, null, Location, TabID);
170 StringBuilder Json = null;
171
172 SetTransparentCorsHeaders(this, Request, Response);
173
174 Response.ContentType = JsonCodec.DefaultContentType;
175
176 if (!await Queue.SyncObj.TryBeginWrite(10000))
177 throw new InternalServerErrorException("Unable to get access to queue.");
178
179 try
180 {
181 if (!(Queue.Queue.First is null))
182 {
183 foreach (string Event in Queue.Queue)
184 {
185 if (Json is null)
186 Json = new StringBuilder("[");
187 else
188 Json.Append(',');
189
190 Json.Append(Event);
191 }
192
193 Queue.Queue.Clear();
194 Queue.Response = null;
195 }
196 else
197 Queue.Response = Response;
198 }
199 finally
200 {
201 await Queue.SyncObj.EndWrite();
202 }
203
204 if (!(Json is null))
205 {
206 timeoutByTabID.Remove(TabID);
207
208 Json.Append(']');
209 await Response.Write(Json.ToString());
210 await Response.SendResponse();
211 await Response.DisposeAsync();
212 }
213 else
214 timeoutByTabID[TabID] = Queue;
215 }
216
217 private static TabQueue Register(HttpRequest Request, HttpResponse Response, WebSocket Socket, string Location, string TabID)
218 {
219 Uri Uri = new Uri(Location);
220 string Resource = Uri.LocalPath;
221 (string, string, string)[] Query = null;
222 Variables Session = Request.Session;
223 string s;
224
225 if (!string.IsNullOrEmpty(Uri.Query))
226 {
227 s = Uri.Query;
228 if (s.StartsWith("?"))
229 s = s.Substring(1);
230
231 string[] Parts = s.Split('&');
232 Query = new (string, string, string)[Parts.Length];
233 int i, j = 0;
234
235 foreach (string Part in Parts)
236 {
237 i = Part.IndexOf('=');
238 if (i < 0)
239 Query[j++] = (Part, string.Empty, string.Empty);
240 else
241 {
242 string s2 = Part.Substring(i + 1);
243 Query[j++] = (Part.Substring(0, i), s2, System.Net.WebUtility.UrlDecode(s2));
244 }
245 }
246 }
247
248 if (eventsByTabID.TryGetValue(TabID, out TabQueue Queue) &&
249 !string.IsNullOrEmpty(Queue.SessionID) &&
250 !(Queue.SyncObj is null))
251 {
252 Queue.WebSocket = Socket;
253 Queue.Uri = Uri;
254 Queue.Query = Query;
255 }
256 else
257 {
258 string HttpSessionID = GetSessionId(Request, Response);
259
260 TabQueue Queue2 = new TabQueue(TabID, HttpSessionID, Session)
261 {
262 WebSocket = Socket,
263 Uri = Uri,
264 Query = Query
265 };
266
267 if (!(Queue is null))
268 {
269 while (!(Queue.Queue.First is null))
270 {
271 Queue2.Queue.AddLast(Queue.Queue.First.Value);
272 Queue.Queue.RemoveFirst();
273 }
274 }
275
276 Queue = Queue2;
277 eventsByTabID[TabID] = Queue;
278 }
279
280 lock (locationByTabID)
281 {
282 if (!locationByTabID.TryGetValue(TabID, out s) || s != Resource)
283 locationByTabID[TabID] = Resource;
284 }
285
286 lock (tabIdsByLocation)
287 {
288 if (!tabIdsByLocation.TryGetValue(Resource, out Dictionary<string, (string, string, string)[]> TabIds))
289 {
290 TabIds = new Dictionary<string, (string, string, string)[]>();
291 tabIdsByLocation[Resource] = TabIds;
292 }
293
294 TabIds[TabID] = Query;
295 }
296
297 Type UserType;
298 object UserObject;
299
300 if (!(Session is null) &&
301 Session.TryGetVariable(" User ", out Variable UserVariable) &&
302 !((UserObject = UserId(UserVariable.ValueObject)) is null))
303 {
304 lock (usersByTabID)
305 {
306 if (!usersByTabID.TryGetValue(TabID, out object Obj2) || !Obj2.Equals(UserObject))
307 usersByTabID[TabID] = UserObject;
308 }
309
310 UserType = UserObject.GetType();
311
312 lock (tabIdsByUser)
313 {
314 if (!tabIdsByUser.TryGetValue(UserType, out Dictionary<object, Dictionary<string, (string, string, string)[]>> UserObjects))
315 {
316 UserObjects = new Dictionary<object, Dictionary<string, (string, string, string)[]>>();
317 tabIdsByUser[UserType] = UserObjects;
318 }
319
320 if (!UserObjects.TryGetValue(UserObject, out Dictionary<string, (string, string, string)[]> TabIds))
321 {
322 TabIds = new Dictionary<string, (string, string, string)[]>();
323 UserObjects[UserObject] = TabIds;
324 }
325
326 TabIds[TabID] = Query;
327 }
328 }
329 else
330 {
331 lock (usersByTabID)
332 {
333 if (!usersByTabID.TryGetValue(TabID, out UserObject))
334 UserObject = null;
335 }
336
337 if (!(UserObject is null))
338 {
339 UserType = UserObject.GetType();
340
341 lock (tabIdsByUser)
342 {
343 if (tabIdsByUser.TryGetValue(UserType, out Dictionary<object, Dictionary<string, (string, string, string)[]>> UserObjects))
344 {
345 if (UserObjects.Remove(UserObject) && UserObjects.Count == 0)
346 tabIdsByUser.Remove(UserType);
347 }
348 }
349 }
350 }
351
352 return Queue;
353 }
354
355 private static object UserId(object User)
356 {
357 if (User is IUser User2)
358 return User2.UserName;
359 else if (User is GenericObject GenObj)
360 {
361 if (GenObj.TryGetFieldValue("UserName", out object UserName))
362 return UserName;
363 else
364 return GenObj.ObjectId;
365 }
366 else if (User is Dictionary<string, IElement> ScriptObj)
367 {
368 if (ScriptObj.TryGetValue("UserName", out IElement UserName))
369 return UserName.AssociatedObjectValue;
370 else
371 return User;
372 }
373 else if (User is Dictionary<string, object> JsonObj)
374 {
375 if (JsonObj.TryGetValue("UserName", out object UserName))
376 return UserName;
377 else
378 return User;
379 }
380 else
381 return User;
382 }
383
384 internal static async Task RegisterWebSocket(WebSocket Socket, string Location, string TabID)
385 {
386 TabQueue Queue = Register(Socket.HttpRequest, Socket.HttpResponse, Socket, Location, TabID);
387 LinkedList<string> ToSend = null;
388
389 if (!await Queue.SyncObj.TryBeginWrite(10000))
390 throw new InternalServerErrorException("Unable to get access to queue.");
391
392 try
393 {
394 if (!(Queue.Queue.First is null))
395 {
396 ToSend = new LinkedList<string>();
397
398 foreach (string s2 in Queue.Queue)
399 ToSend.AddLast(s2);
400
401 Queue.Queue.Clear();
402 }
403 }
404 finally
405 {
406 await Queue.SyncObj.EndWrite();
407 }
408
409 if (!(ToSend is null))
410 {
411 foreach (string s2 in ToSend)
412 await Socket.Send(s2, 4096);
413 }
414 }
415
416 internal static void Ping(string TabID)
417 {
418 if (eventsByTabID.TryGetValue(TabID, out TabQueue TabQueue) && !string.IsNullOrEmpty(TabQueue.SessionID))
419 Gateway.HttpServer?.GetSession(TabQueue.SessionID, false);
420 }
421
422 internal static async Task UnregisterWebSocket(WebSocket Socket, string Location, string TabID)
423 {
424 if (eventsByTabID.TryGetValue(TabID, out TabQueue Queue) && Queue.WebSocket == Socket)
425 {
426 if (!await Queue.SyncObj.TryBeginWrite(10000))
427 throw new InternalServerErrorException("Unable to get access to queue.");
428
429 try
430 {
431 Queue.WebSocket = null;
432 }
433 finally
434 {
435 await Queue.SyncObj.EndWrite();
436 }
437
438 if (Queue.KeepAliveUntil > DateTime.Now)
439 return;
440 }
441
442 Uri Uri = new Uri(Location);
443 Remove(TabID, Uri.LocalPath);
444 }
445
452 public static void KeepTabAlive(string TabID, DateTime KeepAliveUntil)
453 {
454 if (!eventsByTabID.TryGetValue(TabID, out TabQueue Queue))
455 {
456 Queue = new TabQueue(TabID, string.Empty, new Variables());
457 eventsByTabID[TabID] = Queue;
458 }
459
460 DateTime Now = DateTime.Now;
461
462 if (KeepAliveUntil >= Now.AddSeconds(TabIdCacheTimeoutSeconds))
463 {
464 DateTime CurrentKeepAliveTime = Queue.KeepAliveUntil;
465
466 if (CurrentKeepAliveTime < KeepAliveUntil)
467 {
468 Queue.KeepAliveUntil = KeepAliveUntil;
469
470 if (CurrentKeepAliveTime == DateTime.MinValue)
472 }
473 }
474 }
475
476 private static void KeepTabAlive(object State)
477 {
478 if (State is string TabID &&
479 eventsByTabID.TryGetValue(TabID, out TabQueue Queue))
480 {
481 DateTime Now = DateTime.Now;
482
483 if (Queue.KeepAliveUntil < Now)
484 Queue.KeepAliveUntil = DateTime.MinValue;
485 else
487 }
488 }
489
490 private static readonly Cache<string, TabQueue> eventsByTabID = GetTabQueueCache();
491 private static readonly Cache<string, TabQueue> timeoutByTabID = GetTabTimeoutCache();
492 private static readonly Cache<string, ContentQueue> requestsByContentID = GetContentCache();
493 private static readonly Dictionary<string, string> locationByTabID = new Dictionary<string, string>();
494 private static readonly Dictionary<string, object> usersByTabID = new Dictionary<string, object>();
495 private static readonly Dictionary<string, Dictionary<string, (string, string, string)[]>> tabIdsByLocation =
496 new Dictionary<string, Dictionary<string, (string, string, string)[]>>(StringComparer.OrdinalIgnoreCase);
497 private static readonly Dictionary<Type, Dictionary<object, Dictionary<string, (string, string, string)[]>>> tabIdsByUser =
498 new Dictionary<Type, Dictionary<object, Dictionary<string, (string, string, string)[]>>>();
499
500 private static Cache<string, TabQueue> GetTabTimeoutCache()
501 {
502 Cache<string, TabQueue> Result = new Cache<string, TabQueue>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromSeconds(20), true);
503 Result.Removed += TimeoutCacheItem_Removed;
504 return Result;
505 }
506
507 private static async Task TimeoutCacheItem_Removed(object Sender, CacheItemEventArgs<string, TabQueue> e)
508 {
509 if (e.Reason == RemovedReason.NotUsed)
510 {
511 HttpResponse Response = e.Value.Response;
512
513 if (!(Response is null))
514 {
515 try
516 {
517 e.Value.Response = null;
518
519 await Response.Write("[{\"type\":\"NOP\"}]");
520 await Response.SendResponse();
521 }
522 catch (Exception)
523 {
524 // Ignore
525 }
526 finally
527 {
528 await Response.DisposeAsync();
529 }
530 }
531 }
532 }
533
534 private static Cache<string, TabQueue> GetTabQueueCache()
535 {
536 Cache<string, TabQueue> Result = new Cache<string, TabQueue>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromSeconds(TabIdCacheTimeoutSeconds), true);
537 Result.Removed += QueueCacheItem_Removed;
538 return Result;
539 }
540
541 private static Task QueueCacheItem_Removed(object Sender, CacheItemEventArgs<string, TabQueue> e)
542 {
543 TabQueue Queue = e.Value;
544 string TabID = Queue.TabID;
545
546 Remove(TabID, null);
547 Queue.Dispose();
548
549 return Task.CompletedTask;
550 }
551
552 private static void Remove(string TabID, string Resource)
553 {
554 string Location;
555 object User;
556
557 lock (locationByTabID)
558 {
559 if (locationByTabID.TryGetValue(TabID, out Location) && (Resource is null || Location == Resource))
560 locationByTabID.Remove(TabID);
561 else
562 Location = null;
563 }
564
565 if (!(Location is null))
566 {
567 lock (tabIdsByLocation)
568 {
569 if (tabIdsByLocation.TryGetValue(Location, out Dictionary<string, (string, string, string)[]> TabIDs))
570 {
571 if (TabIDs.Remove(TabID) && TabIDs.Count == 0)
572 tabIdsByLocation.Remove(Location);
573 }
574 }
575 }
576
577 lock (usersByTabID)
578 {
579 if (usersByTabID.TryGetValue(TabID, out User))
580 usersByTabID.Remove(TabID);
581 else
582 User = null;
583 }
584
585 if (!(User is null))
586 {
587 Type UserType = User.GetType();
588
589 lock (tabIdsByUser)
590 {
591 if (tabIdsByUser.TryGetValue(UserType, out Dictionary<object, Dictionary<string, (string, string, string)[]>> UserObjects) &&
592 UserObjects.TryGetValue(User, out Dictionary<string, (string, string, string)[]> TabIDs))
593 {
594 if (TabIDs.Remove(TabID) && TabIDs.Count == 0 &&
595 UserObjects.Remove(User) && UserObjects.Count == 0)
596 {
597 tabIdsByUser.Remove(UserType);
598 }
599 }
600 }
601 }
602 }
603
604 private static Cache<string, ContentQueue> GetContentCache()
605 {
606 Cache<string, ContentQueue> Result = new Cache<string, ContentQueue>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromSeconds(90), true);
607 Result.Removed += ContentCacheItem_Removed;
608 return Result;
609 }
610
611 private static async Task ContentCacheItem_Removed(object Sender, CacheItemEventArgs<string, ContentQueue> e)
612 {
613 if (e.Reason != RemovedReason.Manual)
614 {
615 try
616 {
617 HttpResponse Response = e.Value.Response;
618
619 if (!(Response is null))
620 {
621 Response.ContentType = PlainTextCodec.DefaultContentType;
622 await Response.Write("Request took too long to complete.");
623 await Response.SendResponse();
624 }
625 }
626 catch (Exception)
627 {
628 // Ignore
629 }
630 }
631 }
632
639 public static Task ReportAsynchronousResult(string Id, string ContentType, byte[] Result)
640 {
641 return ReportAsynchronousResult(Id, ContentType, Result, false);
642 }
643
651 public static async Task ReportAsynchronousResult(string Id, string ContentType, byte[] Result, bool More)
652 {
653 try
654 {
655 HttpResponse Response;
656
657 lock (requestsByContentID)
658 {
659 if (requestsByContentID.TryGetValue(Id, out ContentQueue Queue))
660 {
661 if (Queue.Response is null)
662 {
663 Queue.ContentType = ContentType;
664 Queue.Content = Result;
665 Queue.More = More;
666 return;
667 }
668
669 Response = Queue.Response;
670
671 if (More)
672 Queue.Response = null;
673 else
674 requestsByContentID.Remove(Id);
675 }
676 else
677 {
678 Queue = new ContentQueue(Id)
679 {
680 ContentType = ContentType,
681 Content = Result,
682 More = More
683 };
684
685 requestsByContentID[Id] = Queue;
686 return;
687 }
688 }
689
690 Response.SetHeader("X-More", More ? "1" : "0");
691 Response.ContentType = ContentType;
692 await Response.Write(Result, 0, Result.Length);
693 await Response.SendResponse();
694 }
695 catch (Exception)
696 {
697 // Ignore
698 }
699 }
700
705 public static string[] GetOpenLocations()
706 {
707 string[] Result;
708
709 lock (tabIdsByLocation)
710 {
711 Result = new string[tabIdsByLocation.Count];
712 tabIdsByLocation.Keys.CopyTo(Result, 0);
713 }
714
715 return Result;
716 }
717
722 public static object[] GetActiveUsers()
723 {
724 List<object> Result = new List<object>();
725
726 lock (tabIdsByUser)
727 {
728 foreach (KeyValuePair<Type, Dictionary<object, Dictionary<string, (string, string, string)[]>>> P in tabIdsByUser)
729 Result.AddRange(P.Value.Keys);
730 }
731
732 return Result.ToArray();
733 }
734
740 public static string[] GetTabIDsForLocation(string Location)
741 {
742 return GetTabIDsForLocation(Location, new KeyValuePair<string, string>[0]);
743 }
744
752 public static string[] GetTabIDsForLocation(string Location, string QueryParameter1, string QueryParameterValue1)
753 {
754 return GetTabIDsForLocation(Location, new KeyValuePair<string, string>(QueryParameter1, QueryParameterValue1));
755 }
756
766 public static string[] GetTabIDsForLocation(string Location, string QueryParameter1, string QueryParameterValue1,
767 string QueryParameter2, string QueryParameterValue2)
768 {
769 return GetTabIDsForLocation(Location,
770 new KeyValuePair<string, string>(QueryParameter1, QueryParameterValue1),
771 new KeyValuePair<string, string>(QueryParameter2, QueryParameterValue2));
772 }
773
780 public static string[] GetTabIDsForLocation(string Location, params KeyValuePair<string, string>[] QueryFilter)
781 {
782 return GetTabIDsForLocation(Location, false, QueryFilter);
783 }
784
793 public static string[] GetTabIDsForLocation(string Location, bool IgnoreCase,
794 string QueryParameter1, string QueryParameterValue1)
795 {
796 return GetTabIDsForLocation(Location, IgnoreCase, new KeyValuePair<string, string>(QueryParameter1, QueryParameterValue1));
797 }
798
809 public static string[] GetTabIDsForLocation(string Location, bool IgnoreCase,
810 string QueryParameter1, string QueryParameterValue1,
811 string QueryParameter2, string QueryParameterValue2)
812 {
813 return GetTabIDsForLocation(Location, IgnoreCase,
814 new KeyValuePair<string, string>(QueryParameter1, QueryParameterValue1),
815 new KeyValuePair<string, string>(QueryParameter2, QueryParameterValue2));
816 }
817
825 public static string[] GetTabIDsForLocation(string Location, bool IgnoreCase, params KeyValuePair<string, string>[] QueryFilter)
826 {
827 lock (tabIdsByLocation)
828 {
829 if (tabIdsByLocation.TryGetValue(Location, out Dictionary<string, (string, string, string)[]> TabIDs))
830 return ProcessQueryFilterLocked(TabIDs, QueryFilter, IgnoreCase);
831 }
832
833 if (eventsByTabID.TryGetValue(Location, out TabQueue Queue))
834 {
835 return ProcessQueryFilterLocked(new Dictionary<string, (string, string, string)[]>()
836 {
837 { Location, Queue.Query }
838 }, QueryFilter, IgnoreCase);
839 }
840 else
841 return new string[0];
842 }
843
849 public static string[] GetTabIDsForUser(object User)
850 {
851 return GetTabIDsForUser(User, null, false, new KeyValuePair<string, string>[0]);
852 }
853
860 public static string[] GetTabIDsForUser(object User, params KeyValuePair<string, string>[] QueryFilter)
861 {
862 return GetTabIDsForUser(User, null, false, QueryFilter);
863 }
864
873 public static string[] GetTabIDsForUser(object User, bool IgnoreCase,
874 string QueryParameter1, string QueryParameterValue1)
875 {
876 return GetTabIDsForUser(User, IgnoreCase, new KeyValuePair<string, string>(QueryParameter1, QueryParameterValue1));
877 }
878
889 public static string[] GetTabIDsForUser(object User, bool IgnoreCase,
890 string QueryParameter1, string QueryParameterValue1,
891 string QueryParameter2, string QueryParameterValue2)
892 {
893 return GetTabIDsForUser(User, IgnoreCase,
894 new KeyValuePair<string, string>(QueryParameter1, QueryParameterValue1),
895 new KeyValuePair<string, string>(QueryParameter2, QueryParameterValue2));
896 }
897
905 public static string[] GetTabIDsForUser(object User, bool IgnoreCase, params KeyValuePair<string, string>[] QueryFilter)
906 {
907 return GetTabIDsForUser(User, null, IgnoreCase, QueryFilter);
908 }
909
916 public static string[] GetTabIDsForUser(object User, string Location)
917 {
918 return GetTabIDsForUser(User, Location, new KeyValuePair<string, string>[0]);
919 }
920
929 public static string[] GetTabIDsForUser(object User, string Location,
930 string QueryParameter1, string QueryParameterValue1)
931 {
932 return GetTabIDsForUser(User, Location,
933 new KeyValuePair<string, string>(QueryParameter1, QueryParameterValue1));
934 }
935
946 public static string[] GetTabIDsForUser(object User, string Location,
947 string QueryParameter1, string QueryParameterValue1,
948 string QueryParameter2, string QueryParameterValue2)
949 {
950 return GetTabIDsForUser(User, Location,
951 new KeyValuePair<string, string>(QueryParameter1, QueryParameterValue1),
952 new KeyValuePair<string, string>(QueryParameter2, QueryParameterValue2));
953 }
954
962 public static string[] GetTabIDsForUser(object User, string Location, params KeyValuePair<string, string>[] QueryFilter)
963 {
964 return GetTabIDsForUser(User, Location, false, QueryFilter);
965 }
966
976 public static string[] GetTabIDsForUser(object User, string Location, bool IgnoreCase,
977 string QueryParameter1, string QueryParameterValue1)
978 {
979 return GetTabIDsForUser(User, Location, IgnoreCase,
980 new KeyValuePair<string, string>(QueryParameter1, QueryParameterValue1));
981 }
982
994 public static string[] GetTabIDsForUser(object User, string Location, bool IgnoreCase,
995 string QueryParameter1, string QueryParameterValue1,
996 string QueryParameter2, string QueryParameterValue2)
997 {
998 return GetTabIDsForUser(User, Location, IgnoreCase,
999 new KeyValuePair<string, string>(QueryParameter1, QueryParameterValue1),
1000 new KeyValuePair<string, string>(QueryParameter2, QueryParameterValue2));
1001 }
1002
1011 public static string[] GetTabIDsForUser(object User, string Location, bool IgnoreCase, params KeyValuePair<string, string>[] QueryFilter)
1012 {
1013 User = UserId(User);
1014 Type T = User.GetType();
1015 string[] Result;
1016
1017 lock (tabIdsByUser)
1018 {
1019 if (tabIdsByUser.TryGetValue(T, out Dictionary<object, Dictionary<string, (string, string, string)[]>> UserObjects) &&
1020 UserObjects.TryGetValue(User, out Dictionary<string, (string, string, string)[]> TabIDs))
1021 {
1022 Result = ProcessQueryFilterLocked(TabIDs, QueryFilter, IgnoreCase);
1023 }
1024 else
1025 return new string[0];
1026 }
1027
1028 if (string.IsNullOrEmpty(Location))
1029 return Result;
1030
1031 List<string> Result2 = new List<string>();
1032
1033 lock (tabIdsByLocation)
1034 {
1035 if (tabIdsByLocation.TryGetValue(Location, out Dictionary<string, (string, string, string)[]> TabIDs))
1036 {
1037 foreach (string TabID in Result)
1038 {
1039 if (TabIDs.ContainsKey(TabID))
1040 Result2.Add(TabID);
1041 }
1042 }
1043 }
1044
1045 return Result2.ToArray();
1046 }
1047
1048 private static string[] ProcessQueryFilterLocked(Dictionary<string, (string, string, string)[]> TabIDs,
1049 KeyValuePair<string, string>[] QueryFilter, bool IgnoreCase)
1050 {
1051 string[] Result;
1052
1053 if (QueryFilter is null || QueryFilter.Length == 0)
1054 {
1055 Result = new string[TabIDs.Count];
1056 TabIDs.Keys.CopyTo(Result, 0);
1057 }
1058 else
1059 {
1060 List<string> Match = new List<string>();
1061 bool Found;
1062 bool IsMatch;
1063
1064 foreach (KeyValuePair<string, (string, string, string)[]> P in TabIDs)
1065 {
1066 IsMatch = true;
1067
1068 foreach (KeyValuePair<string, string> Q in QueryFilter)
1069 {
1070 if (Q.Value is null)
1071 {
1072 Found = true;
1073
1074 if (!(P.Value is null))
1075 {
1076 foreach ((string, string, string) Q2 in P.Value)
1077 {
1078 if (Q2.Item1 == Q.Key)
1079 {
1080 Found = false;
1081 break;
1082 }
1083 }
1084 }
1085
1086 if (!Found)
1087 {
1088 IsMatch = false;
1089 break;
1090 }
1091 }
1092 else
1093 {
1094 Found = false;
1095
1096 if (!(P.Value is null))
1097 {
1098 foreach ((string, string, string) Q2 in P.Value)
1099 {
1100 if (Q2.Item1 == Q.Key &&
1101 (string.Compare(Q2.Item2, Q.Value, IgnoreCase) == 0 ||
1102 string.Compare(Q2.Item3, Q.Value, IgnoreCase) == 0))
1103 {
1104 Found = true;
1105 break;
1106 }
1107 }
1108 }
1109
1110 if (!Found)
1111 {
1112 IsMatch = false;
1113 break;
1114 }
1115 }
1116 }
1117
1118 if (IsMatch)
1119 Match.Add(P.Key);
1120 }
1121
1122 Result = Match.ToArray();
1123 }
1124
1125 return Result;
1126 }
1127
1133 public static string[] GetTabIDsForLocations(params string[] Locations)
1134 {
1135 switch (Locations.Length)
1136 {
1137 case 0:
1138 return new string[0];
1139
1140 case 1:
1141 return GetTabIDsForLocation(Locations[0]);
1142
1143 default:
1144 Dictionary<string, bool> Result = new Dictionary<string, bool>();
1145 Dictionary<string, (string, string, string)[]> TabIDs;
1146
1147 lock (tabIdsByLocation)
1148 {
1149 foreach (string Location in Locations)
1150 {
1151 if (tabIdsByLocation.TryGetValue(Location, out TabIDs))
1152 {
1153 foreach (string TabID in TabIDs.Keys)
1154 Result[TabID] = true;
1155 }
1156 }
1157 }
1158
1159 string[] Result2 = new string[Result.Count];
1160 Result.Keys.CopyTo(Result2, 0);
1161
1162 return Result2;
1163 }
1164 }
1165
1170 public static string[] GetTabIDs()
1171 {
1172 string[] Result;
1173
1174 lock (locationByTabID)
1175 {
1176 Result = new string[locationByTabID.Count];
1177 locationByTabID.Keys.CopyTo(Result, 0);
1178 }
1179
1180 return Result;
1181 }
1182
1187 public static string[] GetTabIDsForUsers()
1188 {
1189 Dictionary<string, bool> Result = new Dictionary<string, bool>();
1190
1191 lock (tabIdsByUser)
1192 {
1193 foreach (Dictionary<object, Dictionary<string, (string, string, string)[]>> P in tabIdsByUser.Values)
1194 {
1195 foreach (Dictionary<string, (string, string, string)[]> P2 in P.Values)
1196 {
1197 foreach (string TabID in P2.Keys)
1198 Result[TabID] = true;
1199 }
1200 }
1201 }
1202
1203 string[] Result2 = new string[Result.Count];
1204 Result.Keys.CopyTo(Result2, 0);
1205 return Result2;
1206 }
1207
1213 public static TabInformation GetTabIDInformation(string TabID)
1214 {
1215 if (eventsByTabID.TryGetValue(TabID, out TabQueue Queue))
1216 return new TabInformation(Queue);
1217 else
1218 return null;
1219 }
1220
1224 public class TabInformation
1225 {
1230 internal TabInformation(TabQueue Queue)
1231 {
1232 this.TabID = Queue.TabID;
1233 this.SessionID = Queue.SessionID;
1234 this.Session = Queue.Session;
1235 this.Uri = Queue.Uri;
1236
1237 this.Query = new Dictionary<string, string>();
1238
1239 if (!(Queue.Query is null))
1240 {
1241 foreach ((string, string, string) Rec in Queue.Query)
1242 this.Query[Rec.Item1] = Rec.Item3;
1243 }
1244 }
1245
1249 public string TabID { get; }
1250
1254 public string SessionID { get; }
1255
1259 public Variables Session { get; }
1260
1264 public Uri Uri { get; }
1265
1269 public Dictionary<string, string> Query { get; }
1270 }
1271
1272 internal class TabQueue : IDisposable
1273 {
1274 public string TabID;
1275 public string SessionID;
1276 public Variables Session;
1277 public MultiReadSingleWriteObject SyncObj;
1278 public LinkedList<string> Queue = new LinkedList<string>();
1279 public HttpResponse Response = null;
1280 public WebSocket WebSocket = null;
1281 public Uri Uri = null;
1282 public DateTime KeepAliveUntil = DateTime.MinValue;
1283 public (string, string, string)[] Query = null;
1284
1285 public TabQueue(string ID, string SessionID, Variables Session)
1286 {
1287 this.SyncObj = new MultiReadSingleWriteObject(this);
1288 this.TabID = ID;
1289 this.SessionID = SessionID;
1290 this.Session = Session;
1291 }
1292
1293 public void Dispose()
1294 {
1295 this.SyncObj?.Dispose();
1296 this.SyncObj = null;
1297
1298 this.Queue?.Clear();
1299 }
1300 }
1301
1302 private class ContentQueue
1303 {
1304 public string ContentID;
1305 public string ContentType;
1306 public HttpResponse Response = null;
1307 public byte[] Content = null;
1308 public bool More = false;
1309
1310 public ContentQueue(string ID)
1311 {
1312 this.ContentID = ID;
1313 }
1314 }
1315
1323 public static Task<int> PushEvent(string[] TabIDs, string Type, object Data)
1324 {
1325 if (Data is string s)
1326 return PushEvent(TabIDs, Type, s, false, null, null);
1327 else
1328 {
1329 s = JSON.Encode(Data, false);
1330 return PushEvent(TabIDs, Type, s, true, null, null);
1331 }
1332 }
1333
1341 public static Task<int> PushEvent(string[] TabIDs, string Type, string Data)
1342 {
1343 return PushEvent(TabIDs, Type, Data, false, null, null);
1344 }
1345
1354 public static Task<int> PushEvent(string[] TabIDs, string Type, string Data, bool DataIsJson)
1355 {
1356 return PushEvent(TabIDs, Type, Data, DataIsJson, null, null);
1357 }
1358
1371 public static async Task<int> PushEvent(string[] TabIDs, string Type, string Data, bool DataIsJson, string UserVariable, params string[] Privileges)
1372 {
1373 int Result = 0;
1374
1375 try
1376 {
1377 StringBuilder Json = new StringBuilder();
1378
1379 Json.Append("{\"type\":\"");
1380 Json.Append(Type);
1381 Json.Append("\",\"data\":");
1382
1383 if (DataIsJson)
1384 Json.Append(Data);
1385 else
1386 {
1387 Json.Append('"');
1388 Json.Append(JSON.Encode(Data));
1389 Json.Append('"');
1390 }
1391
1392 Json.Append('}');
1393
1394 string s = Json.ToString();
1395
1396 if (TabIDs is null)
1397 TabIDs = eventsByTabID.GetKeys();
1398
1399 foreach (string TabID in TabIDs)
1400 {
1401 if (!(TabID is null) && eventsByTabID.TryGetValue(TabID, out TabQueue Queue))
1402 {
1403 if (!string.IsNullOrEmpty(UserVariable))
1404 {
1405 if (!Queue.Session.TryGetVariable(UserVariable, out Variable v) ||
1406 !(v.ValueObject is IUser User))
1407 {
1408 continue;
1409 }
1410
1411 if (!(Privileges is null))
1412 {
1413 bool HasPrivileges = true;
1414
1415 foreach (string Privilege in Privileges)
1416 {
1417 if (!User.HasPrivilege(Privilege))
1418 {
1419 HasPrivileges = false;
1420 break;
1421 }
1422 }
1423
1424 if (!HasPrivileges)
1425 continue;
1426 }
1427 }
1428
1429 if (await Queue.SyncObj.TryBeginWrite(10000))
1430 {
1431 try
1432 {
1433 if (!(Queue.WebSocket is null))
1434 await Queue.WebSocket.Send(Json.ToString(), 4096);
1435 else if (!(Queue.Response is null))
1436 {
1437 try
1438 {
1439 await Queue.Response.Write("[" + Json.ToString() + "]");
1440 await Queue.Response.SendResponse();
1441 await Queue.Response.DisposeAsync();
1442 Queue.Response = null;
1443 }
1444 catch (Exception)
1445 {
1446 // Ignore
1447 }
1448 }
1449 else
1450 Queue.Queue.AddLast(s);
1451 }
1452 finally
1453 {
1454 await Queue.SyncObj.EndWrite();
1455 }
1456 }
1457
1458 timeoutByTabID.Remove(TabID);
1459 Result++;
1460 }
1461 }
1462 }
1463 catch (Exception ex)
1464 {
1465 Log.Exception(ex);
1466 }
1467
1468 return Result;
1469 }
1470
1471 }
1472}
Helps with common JSON-related tasks.
Definition: JSON.cs:14
static string Encode(string s)
Encodes a string for inclusion in JSON.
Definition: JSON.cs:507
const string DefaultContentType
application/json
Definition: JsonCodec.cs:19
Plain text encoder/decoder.
const string DefaultContentType
text/plain
Class representing an event.
Definition: Event.cs:10
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1647
Dictionary< string, string > Query
Query parameters.
The ClientEvents class allows applications to push information asynchronously to web clients connecte...
Definition: ClientEvents.cs:51
static async Task ReportAsynchronousResult(string Id, string ContentType, byte[] Result, bool More)
Reports asynchronously evaluated result back to a client.
static string[] GetTabIDsForUser(object User, params KeyValuePair< string, string >[] QueryFilter)
Gets the Tab IDs of all tabs that a specific user views.
static async Task< int > PushEvent(string[] TabIDs, string Type, string Data, bool DataIsJson, string UserVariable, params string[] Privileges)
Puses an event to a set of Tabs, given their Tab IDs.
static string[] GetTabIDsForUser(object User, string Location)
Gets the Tab IDs of all tabs that a specific user views.
static void KeepTabAlive(string TabID, DateTime KeepAliveUntil)
Keeps a Tab alive, even though it might be temporarily offline or disconnected.
static string[] GetTabIDsForUser(object User, string Location, string QueryParameter1, string QueryParameterValue1)
Gets the Tab IDs of all tabs that a specific user views.
override bool UserSessions
If the resource uses user sessions.
Definition: ClientEvents.cs:78
static string[] GetOpenLocations()
Returns a list of resources that are currently open.
static object[] GetActiveUsers()
Returns a list of active users
bool AllowsGET
If the GET method is allowed.
Definition: ClientEvents.cs:83
static string[] GetTabIDsForLocation(string Location, string QueryParameter1, string QueryParameterValue1)
Gets the Tab IDs of all tabs that display a particular resource.
static TabInformation GetTabIDInformation(string TabID)
Gets information about a Tab, given its ID.
static string[] GetTabIDsForUser(object User, bool IgnoreCase, string QueryParameter1, string QueryParameterValue1, string QueryParameter2, string QueryParameterValue2)
Gets the Tab IDs of all tabs that a specific user views.
override bool HandlesSubPaths
If the resource handles sub-paths.
Definition: ClientEvents.cs:73
static string[] GetTabIDsForLocation(string Location)
Gets the Tab IDs of all tabs that display a particular resource.
const int TabIdCacheTimeoutSeconds
Number of seconds before a Tab ID is purged, unless references or kept alive.
Definition: ClientEvents.cs:55
static Task< int > PushEvent(string[] TabIDs, string Type, object Data)
Puses an event to a set of Tabs, given their Tab IDs.
static string[] GetTabIDsForLocation(string Location, params KeyValuePair< string, string >[] QueryFilter)
Gets the Tab IDs of all tabs that display a particular resource.
static string[] GetTabIDsForUser(object User, string Location, bool IgnoreCase, string QueryParameter1, string QueryParameterValue1, string QueryParameter2, string QueryParameterValue2)
Gets the Tab IDs of all tabs that a specific user views.
static string[] GetTabIDsForUser(object User, string Location, bool IgnoreCase, string QueryParameter1, string QueryParameterValue1)
Gets the Tab IDs of all tabs that a specific user views.
async Task GET(HttpRequest Request, HttpResponse Response)
Executes the POST method on the resource.
Definition: ClientEvents.cs:96
static Task< int > PushEvent(string[] TabIDs, string Type, string Data, bool DataIsJson)
Puses an event to a set of Tabs, given their Tab IDs.
static string[] GetTabIDsForLocation(string Location, bool IgnoreCase, params KeyValuePair< string, string >[] QueryFilter)
Gets the Tab IDs of all tabs that display a particular resource.
const int TabIdCacheTimeoutSecondsHalf
Half of TabIdCacheTimeoutSeconds.
Definition: ClientEvents.cs:60
static string[] GetTabIDsForLocation(string Location, bool IgnoreCase, string QueryParameter1, string QueryParameterValue1, string QueryParameter2, string QueryParameterValue2)
Gets the Tab IDs of all tabs that display a particular resource.
static string[] GetTabIDsForLocations(params string[] Locations)
Gets the Tab IDs of all tabs that display a set of resources.
static string[] GetTabIDsForLocation(string Location, bool IgnoreCase, string QueryParameter1, string QueryParameterValue1)
Gets the Tab IDs of all tabs that display a particular resource.
static string[] GetTabIDsForUser(object User, bool IgnoreCase, string QueryParameter1, string QueryParameterValue1)
Gets the Tab IDs of all tabs that a specific user views.
ClientEvents()
Resource managing asynchronous events to web clients.
Definition: ClientEvents.cs:65
static string[] GetTabIDsForUser(object User)
Gets the Tab IDs of all tabs that a specific user views.
static Task ReportAsynchronousResult(string Id, string ContentType, byte[] Result)
Reports asynchronously evaluated result back to a client.
bool AllowsPOST
If the POST method is allowed.
Definition: ClientEvents.cs:88
static string[] GetTabIDs()
Gets all open Tab IDs.
static string[] GetTabIDsForUser(object User, string Location, bool IgnoreCase, params KeyValuePair< string, string >[] QueryFilter)
Gets the Tab IDs of all tabs that a specific user views.
static string[] GetTabIDsForUser(object User, string Location, params KeyValuePair< string, string >[] QueryFilter)
Gets the Tab IDs of all tabs that a specific user views.
static Task< int > PushEvent(string[] TabIDs, string Type, string Data)
Puses an event to a set of Tabs, given their Tab IDs.
static string[] GetTabIDsForLocation(string Location, string QueryParameter1, string QueryParameterValue1, string QueryParameter2, string QueryParameterValue2)
Gets the Tab IDs of all tabs that display a particular resource.
static string[] GetTabIDsForUser(object User, string Location, string QueryParameter1, string QueryParameterValue1, string QueryParameter2, string QueryParameterValue2)
Gets the Tab IDs of all tabs that a specific user views.
async Task POST(HttpRequest Request, HttpResponse Response)
Executes the POST method on the resource.
static string[] GetTabIDsForUser(object User, bool IgnoreCase, params KeyValuePair< string, string >[] QueryFilter)
Gets the Tab IDs of all tabs that a specific user views.
static string[] GetTabIDsForUsers()
Gets all open Tab IDs for logged in users.
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:126
static DateTime ScheduleEvent(ScheduledEventCallback Callback, DateTime When, object State)
Schedules a one-time event.
Definition: Gateway.cs:3452
The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repe...
Base class for all asynchronous HTTP resources. An asynchronous resource responds outside of the meth...
Represents an HTTP request.
Definition: HttpRequest.cs:18
HttpRequestHeader Header
Request header.
Definition: HttpRequest.cs:134
bool HasData
If the request has data.
Definition: HttpRequest.cs:74
Variables Session
Contains session states, if the resource requires sessions, or null otherwise.
Definition: HttpRequest.cs:164
string SubPath
Sub-path. If a resource is found handling the request, this property contains the trailing sub-path o...
Definition: HttpRequest.cs:146
async Task< object > DecodeDataAsync()
Decodes data sent in request.
Definition: HttpRequest.cs:95
static string GetSessionId(HttpRequest Request, HttpResponse Response)
Gets the session ID used for a request.
static void SetTransparentCorsHeaders(HttpResource Resource, HttpRequest Request, HttpResponse Response)
Sets CORS headers for a resource, allowing it to be embedded in other sites.
const string HttpSessionID
The Cookie Key for HTTP Session Identifiers: "HttpSessionID"
Definition: HttpResource.cs:27
Represets a response of an HTTP client request.
Definition: HttpResponse.cs:21
async Task DisposeAsync()
Closes the connection and disposes of all resources.
async Task SendResponse()
Sends the response back to the client. If the resource is synchronous, there's no need to call this m...
void SetHeader(string FieldName, string Value)
Sets a custom header field value.
async Task Write(byte[] Data)
Returns binary data in the response.
The server encountered an unexpected condition which prevented it from fulfilling the request.
Class handling a web-socket.
Definition: WebSocket.cs:100
HttpResponse HttpResponse
Original HTTP response used when connection was upgrades to a WebSocket connection.
Definition: WebSocket.cs:139
async Task Send(string Payload, int MaxFrameLength)
Sends a text payload, possibly in multiple frames.
Definition: WebSocket.cs:618
HttpRequest HttpRequest
Original HTTP request made to upgrade the connection to a WebSocket connection.
Definition: WebSocket.cs:134
Generic object. Contains a sequence of properties.
Implements an in-memory cache.
Definition: Cache.cs:15
Event arguments for cache item removal events.
ValueType Value
Value of item that was removed.
RemovedReason Reason
Reason for removing the item.
Represents an object that allows single concurrent writers but multiple concurrent readers....
Contains information about a variable.
Definition: Variable.cs:10
Collection of variables.
Definition: Variables.cs:25
virtual bool TryGetVariable(string Name, out Variable Variable)
Tries to get a variable object, given its name.
Definition: Variables.cs:52
GET Interface for HTTP resources.
POST Interface for HTTP resources.
Basic interface for all types of elements.
Definition: IElement.cs:20
Basic interface for a user.
Definition: IUser.cs:7
RemovedReason
Reason for removing the item.
ContentType
DTLS Record content type.
Definition: Enumerations.cs:11