Server Sent Events
Server Sent Events (SSE) can be used to push real-time data down to the web browser in an async manner without blocking threads using the IAsyncIEnumerable interface like so:
public class EventStream : EndpointWithoutRequest
{
public override void Configure()
{
Get("event-stream");
AllowAnonymous();
Options(x => x.RequireCors(p => p.AllowAnyOrigin()));
}
public override async Task HandleAsync(CancellationToken ct)
{
//simply provide any IAsyncEnumerable<T> for the 2nd argument
await Send.EventStreamAsync("my-event", GetDataStream(ct), ct);
}
private async IAsyncEnumerable<object> GetDataStream([EnumeratorCancellation] CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
await Task.Delay(1000);
yield return new { guid = Guid.NewGuid() };
}
}
}
In the browser, the event stream can be subscribed to and consumed using the EventSource object like so:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<script>
const sse = new EventSource('http://localhost:8080/event-stream');
sse.addEventListener('my-event', (e) => console.log(e.data));
</script>
</body>
</html>
The above example demonstrates sending a continuous stream of a single event model type. It is possible to send different types of data in a single stream with the use of the wrapper type StreamItem like so:
public override async Task HandleAsync(CancellationToken c)
{
await Send.EventStreamAsync(GetMultiDataStream(c), c);
async IAsyncEnumerable<StreamItem> GetMultiDataStream([EnumeratorCancellation] CancellationToken ct)
{
long id = 0;
while (!ct.IsCancellationRequested)
{
await Task.Delay(1000);
id++;
if (DateTime.Now.Second % 2 == 1)
yield return new StreamItem(id.ToString(), "odd-second", Guid.NewGuid()); //guide data
else
yield return new StreamItem(id.ToString(), "even-second", "hello!"); //string data
}
}
}
If you are planning to create more than a handful of server-sent-event streams, it's a good idea to enable HTTP2 in kestrel and all upstream servers such as reverse proxies and CDNs so that data can be multiplexed between the web server and client using a low number of tcp connections.
Here's a good read on the subject.