2
0
Files
gin-sse/sse-encoder.go
T
Manu Mtz-Almeida 404710eaa4 Performance improvements.
BenchmarkResponseWriter     1239          1304          +5.25%
BenchmarkFullSSE            1469          915           -37.71%
BenchmarkNoRetrySSE         1187          785           -33.87%
BenchmarkSimpleSSE          961           687           -28.51%

benchmark                   old allocs     new allocs     delta
BenchmarkResponseWriter     9              9              +0.00%
BenchmarkFullSSE            12             3              -75.00%
BenchmarkNoRetrySSE         11             2              -81.82%
BenchmarkSimpleSSE          8              2              -75.00%

benchmark                   old bytes     new bytes     delta
BenchmarkResponseWriter     442           442           +0.00%
BenchmarkFullSSE            462           320           -30.74%
BenchmarkNoRetrySSE         473           337           -28.75%
BenchmarkSimpleSSE          426           314           -26.29%
2015-05-30 11:23:56 +02:00

108 lines
2.3 KiB
Go

// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package sse
import (
"encoding/json"
"fmt"
"io"
"net/http"
"reflect"
"strconv"
"strings"
)
// Server-Sent Events
// W3C Working Draft 29 October 2009
// http://www.w3.org/TR/2009/WD-eventsource-20091029/
const ContentType = "text/event-stream"
var contentType = []string{ContentType}
var noCache = []string{"no-cache"}
type Event struct {
Event string
Id string
Retry uint
Data interface{}
}
func Encode(writer io.Writer, event Event) error {
w := checkWriter(writer)
writeId(w, event.Id)
writeEvent(w, event.Event)
writeRetry(w, event.Retry)
return writeData(w, event.Data)
}
func writeId(w stringWriter, id string) {
if len(id) > 0 {
w.WriteString("id: ")
w.WriteString(escape(id))
w.WriteString("\n")
}
}
func writeEvent(w stringWriter, event string) {
if len(event) > 0 {
w.WriteString("event: ")
w.WriteString(escape(event))
w.WriteString("\n")
}
}
func writeRetry(w stringWriter, retry uint) {
if retry > 0 {
w.WriteString("retry: ")
w.WriteString(strconv.FormatUint(uint64(retry), 10))
w.WriteString("\n")
}
}
func writeData(w stringWriter, data interface{}) error {
w.WriteString("data: ")
switch kindOfData(data) {
case reflect.Struct, reflect.Slice, reflect.Map:
err := json.NewEncoder(w).Encode(data)
if err != nil {
return err
}
w.WriteString("\n")
default:
text := fmt.Sprint(data)
w.WriteString(escape(text))
w.WriteString("\n\n")
}
return nil
}
func (r Event) Write(w http.ResponseWriter) error {
header := w.Header()
header["Content-Type"] = contentType
if _, exist := header["Cache-Control"]; !exist {
header["Cache-Control"] = noCache
}
return Encode(w, r)
}
func kindOfData(data interface{}) reflect.Kind {
value := reflect.ValueOf(data)
valueType := value.Kind()
if valueType == reflect.Ptr {
valueType = value.Elem().Kind()
}
return valueType
}
func escape(str string) string {
// any-char = %x0000-0009 / %x000B-000C / %x000E-10FFFF
// ; a Unicode character other than U+000A LINE FEED (LF) or U+000D CARRIAGE RETURN (CR)
str = strings.Replace(str, "\n", "\\n", -1)
str = strings.Replace(str, "\r", "\\r", -1)
return str
}