Skip to content

Instantly share code, notes, and snippets.

@jinroh
Forked from artisonian/.gitignore
Created January 3, 2014 22:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jinroh/8248025 to your computer and use it in GitHub Desktop.
Save jinroh/8248025 to your computer and use it in GitHub Desktop.
eventsource
go-eventsource
client/client

go-eventsource

A demo of server-sent events (i.e., EventSource).

Hat tip to athoune/broadcast-event-source for an example of SSE in Go.

Example with cURL:

curl -X POST -H "Content-Type: application/json" http://localhost:8888/update -d '{"text":"Hello from cURL"}'

Example with Go client:

go run client/client.go
$(function() {
var source = new EventSource('/events');
source.onopen = function (event) {
console.log("eventsource connection open");
};
source.onerror = function() {
if (event.target.readyState === 0) {
console.log("reconnecting to eventsource");
} else {
console.log("eventsource error");
}
};
source.onmessage = function(event) {
$('<div>', {
text: event.data,
css: {
display: "none"
}
})
.prependTo("#messages")
.show("fast")
;
};
});
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
)
func main() {
b, err := json.Marshal(map[string]interface{}{
"text": fmt.Sprintf("Hello from go client at %s", time.Now().Format(time.Kitchen)),
})
if err != nil {
log.Fatal(err)
}
buf := bytes.NewBuffer(b)
resp, err := http.Post("http://localhost:8888/update", "application/json", buf)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(body))
}
package main
import (
"encoding/json"
"fmt"
"net/http"
)
const (
msgBuf = 10
)
type Message struct {
Text string
}
type Response map[string]interface{}
func (r Response) String() (s string) {
b, err := json.Marshal(r)
if err != nil {
s = ""
return
}
s = string(b)
return
}
type Broker struct {
subscribers map[chan []byte]bool
}
func (b *Broker) Subscribe() chan []byte {
ch := make(chan []byte, msgBuf)
b.subscribers[ch] = true
return ch
}
func (b *Broker) Unsubscribe(ch chan []byte) {
delete(b.subscribers, ch)
}
func (b *Broker) Publish(msg []byte) {
for ch := range b.subscribers {
ch <- msg
}
}
func NewBroker() *Broker {
return &Broker{make(map[chan []byte]bool)}
}
var msgBroker *Broker
func messageHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
if r.Header.Get("Content-Type")[:16] != "application/json" {
http.Error(w, "Content-Type must be application/json", http.StatusNotAcceptable)
return
}
var m Message
dec := json.NewDecoder(r.Body)
if err := dec.Decode(&m); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
msgBroker.Publish([]byte(m.Text))
if r.Header.Get("Accept") == "application/json" {
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, Response{"success": true, "message": "OK"})
return
}
fmt.Fprintln(w, "OK")
}
func timerEventSource(w http.ResponseWriter, r *http.Request) {
f, ok := w.(http.Flusher)
if !ok {
http.Error(w, "streaming unsupported", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
ch := msgBroker.Subscribe()
defer msgBroker.Unsubscribe(ch)
for {
msg := <-ch
fmt.Fprintf(w, "data: Message: %s\n\n", msg)
f.Flush()
}
}
func main() {
msgBroker = NewBroker()
http.HandleFunc("/update", messageHandler)
http.HandleFunc("/events", timerEventSource)
http.Handle("/", http.FileServer(http.Dir("static")))
err := http.ListenAndServe("localhost:8888", nil)
if err != nil {
panic(err)
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>EventSource Demo</title>
<link rel="stylesheet" href="style.css" type="text/css">
<script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
</head>
<body>
<div class="container">
<h1>Messages</h1>
<div id="messages"></div>
</div>
<script src="app.js"></script>
</body>
</html>
* {
box-sizing: border-box;
}
body {
background-color: #eee;
font: 100%/1.5 "Source Sans Pro", "Helvetica Neue", Helvetica, sans-serif;
padding-top: 2em;
}
.container {
max-width: 720px;
margin: 0 auto;
padding: 20px;
background-color: #fff;
color: #333;
border: 1px solid #ddd;
}
h1 {
margin-top: 0;
}
#messages {
max-height: 480px;
overflow-y: auto;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment