public async Task <string> EventSource() { // use the event stream content type, as per specification HttpContext.Response.ContentType = "text/event-stream"; // get the last event id out of the header string lastEventIdString = HttpContext.Request.Headers["Last-Event-ID"].FirstOrDefault(); int temp; int? lastEventId = null; if (lastEventIdString != null && int.TryParse(lastEventIdString, out temp)) { lastEventId = temp; } string remoteIp = HttpContext.Connection.RemoteIpAddress.ToString(); // open the current request stream for writing. // Use UTF-8 encoding, and do not close the stream when disposing. using (var clientStream = new StreamWriter(HttpContext.Response.Body, Encoding.UTF8, 1024, true) { AutoFlush = true }) { // subscribe to the heartbeat event. Elsewhere, a timer will push updates to this event periodically. using (EventSubscription <EventSourceUpdate> subscription = eventsService.SubscribeTo <EventSourceUpdate>("heartbeat")) { try { logger.LogInformation($"Opened event source stream to address: {remoteIp}"); await clientStream.WriteLineAsync($":connected {DateTimeOffset.Now.ToString("o")}"); // If a last event id is given, pump out any intermediate events here. if (lastEventId != null) { // We're not doing anything that stores events in this heartbeat demo, // so do nothing here. } // start pumping out events as they are pushed to the subscription queue while (true) { // asynchronously wait for an event to fire before continuing this loop. // this is implemented with a semaphore slim using the async wait, so it // should play nice with the async framework. EventSourceUpdate update = await subscription.WaitForData(); // push the update down the request stream to the client if (update != null) { string updateString = update.ToString(); await clientStream.WriteAsync(updateString); } if (HttpContext.RequestAborted.IsCancellationRequested == true) { break; } } } catch (Exception e) { // catch client closing the connection logger.LogInformation($"Closed event source stream from {remoteIp}. Message: {e.Message}"); } } } return(":closed"); }