source: binary-improvements2/MapRendering/Web/SSE/SseHandler.cs@ 388

Last change on this file since 388 was 387, checked in by alloc, 3 years ago

Big refactoring in Web to pass around a Context instead of a bunch of individual arguments all the time

File size: 3.7 KB
RevLine 
[367]1using System;
2using System.Net;
3using System.Collections.Generic;
4using System.Reflection;
5using System.Threading;
6using AllocsFixes.NetConnections.Servers.Web.Handlers;
7
8// Implemented following HTML spec
9// https://html.spec.whatwg.org/multipage/server-sent-events.html
10
11namespace AllocsFixes.NetConnections.Servers.Web.SSE {
[382]12 public class SseHandler : AbsHandler {
[367]13 private readonly Dictionary<string, EventBase> events = new CaseInsensitiveStringDictionary<EventBase> ();
[382]14
[367]15 private ThreadManager.ThreadInfo queueThead;
16 private readonly AutoResetEvent evSendRequest = new AutoResetEvent (false);
17 private bool shutdown;
18
19 public SseHandler (string _moduleName = null) : base (_moduleName) {
[382]20 Type[] ctorTypes = { typeof (SseHandler) };
21 object[] ctorParams = { this };
[367]22
23 foreach (Type t in Assembly.GetExecutingAssembly ().GetTypes ()) {
24 if (!t.IsAbstract && t.IsSubclassOf (typeof (EventBase))) {
25 ConstructorInfo ctor = t.GetConstructor (ctorTypes);
26 if (ctor != null) {
[382]27 EventBase apiInstance = (EventBase)ctor.Invoke (ctorParams);
28 AddEvent (apiInstance.Name, apiInstance);
[367]29 }
30 }
31 }
32 }
33
34 public override void SetBasePathAndParent (Web _parent, string _relativePath) {
35 base.SetBasePathAndParent (_parent, _relativePath);
[382]36
[367]37 queueThead = ThreadManager.StartThread ("SSE-Processing_" + urlBasePath, QueueProcessThread, ThreadPriority.BelowNormal,
38 _useRealThread: true);
39 }
40
41 public override void Shutdown () {
42 base.Shutdown ();
43 shutdown = true;
44 SignalSendQueue ();
45 }
46
[382]47 public void AddEvent (string _eventName, EventBase _eventInstance) {
[367]48 events.Add (_eventName, _eventInstance);
49 WebPermissions.Instance.AddKnownModule ("webevent." + _eventName, _eventInstance.DefaultPermissionLevel ());
50 }
51
[387]52 public override void HandleRequest (RequestContext _context) {
53 string eventName = _context.RequestPath.Remove (0, urlBasePath.Length);
[367]54
55 if (!events.TryGetValue (eventName, out EventBase eventInstance)) {
56 Log.Out ($"Error in {nameof (SseHandler)}.HandleRequest(): No handler found for event \"{eventName}\"");
[387]57 _context.Response.StatusCode = (int)HttpStatusCode.NotFound;
[367]58 return;
59 }
60
[387]61 if (!IsAuthorizedForEvent (eventName, _context.PermissionLevel)) {
62 _context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
63 if (_context.Connection != null) {
[367]64 //Log.Out ($"{nameof(SseHandler)}: user '{user.SteamID}' not allowed to access '{eventName}'");
65 }
66
67 return;
68 }
69
70 try {
[387]71 eventInstance.AddListener (_context.Response);
[367]72
73 // Keep the request open
[387]74 _context.Response.SendChunked = true;
[367]75
[387]76 _context.Response.AddHeader ("Content-Type", "text/event-stream");
77 _context.Response.OutputStream.Flush ();
[367]78 } catch (Exception e) {
79 Log.Error ($"Error in {nameof (SseHandler)}.HandleRequest(): Handler {eventInstance.Name} threw an exception:");
80 Log.Exception (e);
[387]81 _context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
[367]82 }
83 }
84
[382]85 private bool IsAuthorizedForEvent (string _eventName, int _permissionLevel) {
[367]86 return WebPermissions.Instance.ModuleAllowedWithLevel ("webevent." + _eventName, _permissionLevel);
87 }
88
89 private void QueueProcessThread (ThreadManager.ThreadInfo _threadInfo) {
[382]90 while (!shutdown && !_threadInfo.TerminationRequested ()) {
91 evSendRequest.WaitOne (500);
[367]92
[382]93 foreach (KeyValuePair<string, EventBase> kvp in events) {
94 try {
[367]95 kvp.Value.ProcessSendQueue ();
[382]96 } catch (Exception e) {
97 Log.Error ($"SSE ({kvp.Key}): Error processing send queue");
98 Log.Exception (e);
[367]99 }
100 }
101 }
102 }
103
104 public void SignalSendQueue () {
105 evSendRequest.Set ();
106 }
107 }
108}
Note: See TracBrowser for help on using the repository browser.