Server Sent Events

Live on this server

A live ticker that receives prices pushed by the server.

ServerSentEventsSample.cs78 lines
using GenHTTP.Api.Content;
using GenHTTP.Api.Protocol;

using GenHTTP.Modules.IO;
using GenHTTP.Modules.Layouting;
using GenHTTP.Modules.ServerSentEvents;

public static class ServerSentEventsSample
{

    public static IHandlerBuilder Create()
    {
        /*
         *
         * A live ticker: the server pushes the price of a (made up) stock to
         * the browser every second, without the client polling for it.
         *
         * See https://genhttp.org/documentation/content/handlers/server-sent-events/
         *
         */

        var quotes = EventSource.Create()
                                .Generator(SendQuotesAsync);

        var client = Content.From(Resource.FromString(ClientPage).Type(ContentType.TextHtml));

        return Layout.Create()
                     .Index(client) // GET /samples/sse/
                     .Add("quotes", quotes); // GET /samples/sse/quotes/
    }

    public record Quote(string Symbol, decimal Price, decimal Change);

    private static async ValueTask SendQuotesAsync(IEventConnection connection)
    {
        var price = 100.0m;
        var id = 0;

        while (connection.Connected)
        {
            var change = Math.Round((decimal)(Random.Shared.NextDouble() - 0.5), 2);

            price += change;

            // serialized to JSON, the browser listens for the "quote" event type
            await connection.DataAsync(new Quote("GENH", price, change), eventType: "quote", eventId: $"{id++}");

            await Task.Delay(1000);
        }
    }

    private const string ClientPage =
        """
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="utf-8">
            <title>Ticker</title>
        </head>
        <body>
            <h1>GENH <span id="price">-</span></h1>
            <p id="change"></p>
            <script>
                // reconnects automatically if the connection drops
                const events = new EventSource('quotes/');

                events.addEventListener('quote', (event) => {
                    const quote = JSON.parse(event.data);

                    document.getElementById('price').textContent = quote.price.toFixed(2);
                    document.getElementById('change').textContent = `${quote.change >= 0 ? '+' : ''}${quote.change.toFixed(2)}`;
                });
            </script>
        </body>
        </html>
        """;

}

Try it

Send the requests to the sample running on this server - change the path, headers or body as you like, or copy them as curl commands.

  1. Open the ticker

    PAGE /samples/sse/ Open
  2. Watch the raw event stream

    curl -N https://genhttp.org/samples/sse/quotes/