using System.Text; using ChatTwo.Http.MessageProtocol; using Newtonsoft.Json; using WatsonWebserver.Core; namespace ChatTwo.Http; public class EventServer { private bool Stopping; private bool Finished; private readonly CancellationToken Token; public readonly Stack OutboundStack = new(); private long Index; public EventServer(CancellationToken token) { Token = token; } public async Task HandleEventLoop(HttpContextBase ctx) { try { ctx.Response.Headers.Add("Content-Type", "text/event-stream"); ctx.Response.Headers.Add("Cache-Control", "no-cache"); ctx.Response.Headers.Add("Connection", "keep-alive"); ctx.Response.ChunkedTransfer = true; while (!Token.IsCancellationRequested && !Stopping) { await Task.Delay(10, Token); if (Token.IsCancellationRequested) return; if (!OutboundStack.TryPop(out var message)) continue; var data = JsonConvert.SerializeObject(message); await ctx.Response.SendChunk(Encoding.UTF8.GetBytes($"id: {Index}\ndata: {data}\n\n"), Token); Index++; } } catch (TaskCanceledException) { // Ignore } catch (Exception ex) { Plugin.Log.Error(ex, "Event queued failed to continue"); } finally { // No Content (204) didn't work for Firefox, so manually closing the connection on client side await ctx.Response.SendFinalChunk("data: closing\nevent: close\n\n"u8.ToArray()); Finished = true; } } public async ValueTask DisposeAsync() { Stopping = true; var timeout = 1000; // 1000ms while (timeout > 0) { if (Finished) break; timeout -= 100; await Task.Delay(100); Plugin.Log.Debug("Sleeping because EventServer still alive"); } } }