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

Last change on this file since 384 was 382, checked in by alloc, 2 years ago

Switched to use SpaceWizards.HttpListener

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