andersch.dev

<2025-03-26 Wed>
[ web ]

Server-Sent Events (SSE)

Server-Sent Events (SSE) allows a web server to push real-time updates to a client (i.e. a web browser) over a single, long-lived HTTP connection.

Unlike bidirectional WebSockets, SSE is unidirectional, meaning communication only flows from the server to the client.

Example using Go

package main

import "fmt"; import "log"; import "net/http"; import "time";

func sseHandler(w http.ResponseWriter, r *http.Request) {
    // sse headers
    w.Header().Set("Content-Type",  "text/event-stream")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection",    "keep-alive")

    // ticker to send events every second
    ticker := time.NewTicker(1 * time.Second)
    defer ticker.Stop()

    done := r.Context().Done() // channel to signal when client disconnects

    for {
        select {
        case <-ticker.C: // clock
            // send current time as an event
            currentTime := time.Now().Format(time.RFC3339)
            fmt.Fprintf(w, "data: %s\n\n", currentTime)

            // flush response writer to send data immediately
            if f, ok := w.(http.Flusher); ok {
                f.Flush()
            } else {
                log.Println("Streaming unsupported!")
                return
            }

        case <-done:
            log.Println("Client disconnected")
            return
        }
    }
}

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        http.ServeFile(w, r, "index.html")
    })

    http.HandleFunc("/events", sseHandler)
    log.Println("SSE server listening on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
<!DOCTYPE html>
<html>
<head>
    <title>SSE Example</title>
</head>
<body>
    <h1>SSE Example</h1>
    <div id="output"></div>

    <script>
        const outputDiv = document.getElementById('output');

        const eventSource = new EventSource('/events'); // establish SSE connection

        eventSource.onmessage = (event) => {
            outputDiv.innerHTML += `<p>Received: ${event.data}</p>`;
        };

        eventSource.onerror = (error) => {
            console.error("SSE error:", error);
            eventSource.close(); // Close the connection on error
        };

        eventSource.onopen = () => { console.log("SSE connection opened"); }
    </script>
</body>
</html>