Thoughts of a software developer

09.02.2019 18:33 | Modified 21.09. 21:23
Exploring Server-sent Events (SSE)

I’ve used websockets before, and it’s a handy way to communicate between client and server. The thing is, it’s tcp, not http and demands a solution on it’s own. If you only need to update the client from server and not the other way around, there is an other way, Server Side Events.

According to Wikipedia: Server-sent events (SSE) is a technology enabling a browser to receive automatic updates from a server via HTTP connection. The Server-Sent Events EventSource API is standardized as part of HTML5 by the W3C.

I made a server to send messages to clients and two, go- and javascript clients. The project is at https://github.com/jelinden/server-side-events.

For handling the low level server implementation I used github.com/r3labs/sse.

import "github.com/r3labs/sse"

func main() {
    server = sse.New()
    server.AutoReplay = false
    server.CreateStream("messages")
    mux := http.NewServeMux()
    mux.HandleFunc("/events", sseHandler)
    go dataSender(server)
    log.Println("starting server at port 8080")
    http.ListenAndServe(":8080", mux)
}

We define a new server with no autoreplay (sends only from the moment the client joins the feed), define the handler and then let go’s default http server deal with the serving.

Now the actual messages to client, we send a growing number 10 times in a second:

func dataSender(server *sse.Server) {
    i := 0
    for {
        server.Publish("messages", &sse.Event{
            Data: []byte(strconv.Itoa(i)),
        })
        i++
        time.Sleep(100 * time.Millisecond)
    }
}

Go client is really simple, we subscribe and then just print everything the server sends:

func main() {
    client := sse.NewClient("http://localhost:8080/events")
    client.Headers["X-Forwarded-For"] = "127.0.0.1"
    client.Subscribe("messages", func(msg *sse.Event) {
        fmt.Printf("\r%v", string(msg.Data))
    })
}

I the javascript case, we are also serving an index.html file, but otherwise the functionality is the same as with the original server. Connecting to the server from the javascript client is simple new EventSource handles the connection and then source.onmessage listens for messages. Messages are updated to an own div in the page.

 <header>
        <title>Server Side Events</title>
        <script type="text/javascript">
            if (typeof (EventSource) !== "undefined") {
                var source = new EventSource("/events?stream=messages");
                source.onmessage = function (event) {
                    document.getElementById("sse").innerHTML = "<br/>" + event.data + "<br/>";
                };
            } else {
                document.getElementById("sse").innerHTML = "Your browser does not support server side events";
            }
        </script>
    </header>
    <body>
        <br />
        <div>Hello Server Side Events!</div>
        <div id="sse"></div>
    </body>

Small screencast from SSE in action:

Browser support: All usable browsers support Server-sent events. SSE Browser compability

Links and sources: