Skip to content

Commit

Permalink
Adds support for Server-Sent Events
Browse files Browse the repository at this point in the history
  • Loading branch information
manucorporat committed May 12, 2015
1 parent 59c836e commit 470b7e1
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 2 deletions.
22 changes: 20 additions & 2 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package gin

import (
"errors"
"io"
"math"
"net/http"
"strings"
Expand Down Expand Up @@ -379,16 +380,33 @@ func (c *Context) File(filepath string) {
http.ServeFile(c.Writer, c.Request, filepath)
}

func (c *Context) Stream(step func(w http.ResponseWriter)) {
func (c *Context) SSEvent(name string, message interface{}) {
render.WriteSSEvent(c.Writer, name, message)
}

func (c *Context) Header(code int, headers map[string]string) {
if len(headers) > 0 {
header := c.Writer.Header()
for key, value := range headers {
header.Set(key, value)
}
}
c.Writer.WriteHeader(code)
}

func (c *Context) Stream(step func(w io.Writer) bool) {
w := c.Writer
clientGone := w.CloseNotify()
for {
select {
case <-clientGone:
return
default:
step(w)
keepopen := step(w)
w.Flush()
if !keepopen {
return
}
}
}
}
Expand Down
58 changes: 58 additions & 0 deletions render/ssevent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package render

import (
"encoding/json"
"fmt"
"net/http"
"reflect"
)

type sseRender struct{}

var SSEvent Render = sseRender{}

func (_ sseRender) Render(w http.ResponseWriter, code int, data ...interface{}) error {
eventName := data[0].(string)
obj := data[1]
return WriteSSEvent(w, eventName, obj)
}

func WriteSSEvent(w http.ResponseWriter, eventName string, data interface{}) error {
header := w.Header()
if len(header.Get("Content-Type")) == 0 {
w.Header().Set("Content-Type", "text/event-stream")
}
var stringData string
switch typeOfData(data) {
case reflect.Struct, reflect.Slice:
if jsonBytes, err := json.Marshal(data); err == nil {
stringData = string(jsonBytes)
} else {
return err
}
case reflect.Ptr:
stringData = escape(fmt.Sprintf("%v", &data)) + "\n"
default:
stringData = escape(fmt.Sprintf("%v", data)) + "\n"
}
_, err := fmt.Fprintf(w, "event: %s\ndata: %s\n", escape(eventName), stringData)
return err
}

func typeOfData(data interface{}) reflect.Kind {
value := reflect.ValueOf(data)
valueType := value.Kind()
if valueType == reflect.Ptr {
newValue := value.Elem().Kind()
if newValue == reflect.Struct || newValue == reflect.Slice {
return newValue
} else {
return valueType
}
}
return valueType
}

func escape(str string) string {
return str
}

0 comments on commit 470b7e1

Please sign in to comment.