Skip to content

Instantly share code, notes, and snippets.

@fcarriedo
Last active December 18, 2015 09:29
Show Gist options
  • Save fcarriedo/5762125 to your computer and use it in GitHub Desktop.
Save fcarriedo/5762125 to your computer and use it in GitHub Desktop.
HTML5 Server sent events demo implementation
<div class="msg">
<h3>Msg</h3>
<p>This is the message: </p>
<p><i>{{.}}</i></p>
</div>
<html>
<head>
<style>
body {font-family: helvetica;}
#msgs .msg {float: left;}
.msg {font-size: 8pt; color: #666; background-color: #eee; padding: 2px; border-radius: 5px; width: 200px; margin: 0px 3px 3px 0px;}
</style>
<script>
window.onload = function() {
// Create the event source
var source = new EventSource("/stream");
// On open/close events of the stream
source.onopen = function () { console.log('opened..') };
source.onerror = function () { console.log('error'); /*source.close();*/ };
// On message handling
var msgContainer = document.getElementById('msgs');
source.onmessage = function (evt) {
msgContainer.insertAdjacentHTML('beforeend', evt.data);
}
// Capture a specific event
source.addEventListener('foo-evt', function(evt) {
console.log('Got an event! : ' + evt.data);
});
};
</script>
</head>
<body>
<h1 id='title'>Server-sent events > Hello World!</h1>
<div id='msgs'>
</div>
</body>
</html>
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"strings"
"text/template"
"time"
)
type message struct {
Id int
Event string
Data []string
}
var elems = []int{1, 2, 3}
// Event format that conforms to the spec as defined in:
// http://www.w3.org/TR/2011/WD-eventsource-20110208/#event-stream-interpretation
const evtMsgFormat = "" +
"{{if .Id}}id: {{.Id}}\n{{end}}" + // optional
"{{if .Event}}event: {{.Event}}\n{{end}}" + // optional
"{{range .Data}}data: {{.}}\n{{end}}" + // required
"\r\n" // Empty line delimits message (preferably '\r\n')
// Creates a new message
func newMsg(id int, evt, data string) message {
return message{Id: id, Event: evt, Data: strings.Split(data, "\r\n")}
}
// Templates
var evtFormatTmpl = template.Must(template.New("evtTmpl").Parse(evtMsgFormat))
var dataTmpls = template.Must(template.ParseFiles("data-tmpl.html"))
func handleStream(w http.ResponseWriter, r *http.Request) {
fmt.Println("Request to handle stream")
// Write the event stream headers
w.Header().Set("Content-Type", "text/event-stream")
fmt.Fprintf(w, "retry: %d\r\n", 5000) // Retry policy: 5 sec
for _, i := range elems {
for _, j := range elems {
var buf bytes.Buffer
dataTmpls.ExecuteTemplate(&buf, "data-tmpl.html", fmt.Sprintf("some dynamic data: %d", j))
msg := newMsg(j, "", buf.String())
fmt.Printf("Sending: %#v\n", msg)
sendMsg(w, msg)
// Also send an event under certain conditions
if i == 2 && j == 2 {
msg = newMsg(i, "foo-evt", fmt.Sprintf("This is an event! %d", j))
sendMsg(w, msg)
}
}
time.Sleep(3 * time.Second)
}
}
func sendMsg(w io.Writer, msg message) {
// Apply the server sent message format
mw := io.MultiWriter(os.Stdout, w)
if err := evtFormatTmpl.Execute(mw, msg); err != nil {
panic("Template couldn't be executed")
}
// Flush so that it gets pushed immediately to the client
w.(http.Flusher).Flush()
}
func main() {
http.HandleFunc("/stream", handleStream)
http.Handle("/monitor/", http.StripPrefix("/monitor", http.FileServer(http.Dir("."))))
fmt.Println("Server up & running in port: 8089")
http.ListenAndServe(":8089", nil)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment