Initial commit
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
package sse
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
Event string
|
||||
Id string
|
||||
Retry uint
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
func Encode(w io.Writer, event Event) error {
|
||||
writeId(w, event.Id)
|
||||
writeEvent(w, event.Event)
|
||||
writeRetry(w, event.Retry)
|
||||
return writeData(w, event.Data)
|
||||
}
|
||||
|
||||
func writeId(w io.Writer, id string) {
|
||||
if len(id) > 0 {
|
||||
w.Write([]byte("id: "))
|
||||
w.Write([]byte(escape(id)))
|
||||
w.Write([]byte("\n"))
|
||||
}
|
||||
}
|
||||
|
||||
func writeEvent(w io.Writer, event string) {
|
||||
if len(event) > 0 {
|
||||
w.Write([]byte("event: "))
|
||||
w.Write([]byte(escape(event)))
|
||||
w.Write([]byte("\n"))
|
||||
}
|
||||
}
|
||||
|
||||
func writeRetry(w io.Writer, retry uint) {
|
||||
if retry > 0 {
|
||||
fmt.Fprintf(w, "retry: %d\n", retry)
|
||||
}
|
||||
}
|
||||
|
||||
func writeData(w io.Writer, data interface{}) error {
|
||||
w.Write([]byte("data: "))
|
||||
switch typeOfData(data) {
|
||||
case reflect.Struct, reflect.Slice, reflect.Map:
|
||||
err := json.NewEncoder(w).Encode(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.Write([]byte("\n"))
|
||||
default:
|
||||
text := fmt.Sprint(data)
|
||||
fmt.Fprint(w, escape(text), "\n\n")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func typeOfData(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 {
|
||||
str = strings.Replace(str, "\n", "\\n", -1)
|
||||
str = strings.Replace(str, "\r", "\\r", -1)
|
||||
return str
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
package sse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEncodeOnlyData(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Data: "junk\n\njk\nid:fake",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "data: junk\\n\\njk\\nid:fake\n\n")
|
||||
}
|
||||
|
||||
func TestEncodeWithEvent(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Event: "t\n:<>\r\test",
|
||||
Data: "junk\n\njk\nid:fake",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "event: t\\n:<>\\r\test\ndata: junk\\n\\njk\\nid:fake\n\n")
|
||||
}
|
||||
|
||||
func TestEncodeWithId(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Id: "t\n:<>\r\test",
|
||||
Data: "junk\n\njk\nid:fa\rke",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "id: t\\n:<>\\r\test\ndata: junk\\n\\njk\\nid:fa\\rke\n\n")
|
||||
}
|
||||
|
||||
func TestEncodeWithRetry(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Retry: 11,
|
||||
Data: "junk\n\njk\nid:fake\n",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "retry: 11\ndata: junk\\n\\njk\\nid:fake\\n\n\n")
|
||||
}
|
||||
|
||||
func TestEncodeWithEverything(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Event: "abc",
|
||||
Id: "12345",
|
||||
Retry: 10,
|
||||
Data: "some data",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "id: 12345\nevent: abc\nretry: 10\ndata: some data\n\n")
|
||||
}
|
||||
|
||||
func TestEncodeMap(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Event: "a map",
|
||||
Data: map[string]interface{}{
|
||||
"foo": "b\n\rar",
|
||||
"bar": "id: 2",
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "event: a map\ndata: {\"bar\":\"id: 2\",\"foo\":\"b\\n\\rar\"}\n\n")
|
||||
}
|
||||
|
||||
func TestEncodeSlice(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Event: "a slice",
|
||||
Data: []interface{}{1, "text", map[string]interface{}{"foo": "bar"}},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "event: a slice\ndata: [1,\"text\",{\"foo\":\"bar\"}]\n\n")
|
||||
}
|
||||
|
||||
func TestEncodeStruct(t *testing.T) {
|
||||
myStruct := struct {
|
||||
A int
|
||||
B string `json:"value"`
|
||||
}{1, "number"}
|
||||
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Event: "a struct",
|
||||
Data: myStruct,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "event: a struct\ndata: {\"A\":1,\"value\":\"number\"}\n\n")
|
||||
|
||||
w.Reset()
|
||||
err = Encode(w, Event{
|
||||
Event: "a struct",
|
||||
Data: &myStruct,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "event: a struct\ndata: {\"A\":1,\"value\":\"number\"}\n\n")
|
||||
}
|
||||
|
||||
func TestEncodeInteger(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Event: "an integer",
|
||||
Data: 1,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "event: an integer\ndata: 1\n\n")
|
||||
}
|
||||
|
||||
func TestEncodeFloat(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Event: "Float",
|
||||
Data: 1.5,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "event: Float\ndata: 1.5\n\n")
|
||||
}
|
||||
|
||||
func TestEncodeStream(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
|
||||
Encode(w, Event{
|
||||
Event: "float",
|
||||
Data: 1.5,
|
||||
})
|
||||
|
||||
Encode(w, Event{
|
||||
Id: "123",
|
||||
Data: map[string]interface{}{"foo": "bar", "bar": "foo"},
|
||||
})
|
||||
|
||||
Encode(w, Event{
|
||||
Id: "124",
|
||||
Event: "chat",
|
||||
Data: "hi! dude",
|
||||
})
|
||||
fmt.Println(w.String())
|
||||
assert.Equal(t, w.String(), "event: float\ndata: 1.5\n\nid: 123\ndata: {\"bar\":\"foo\",\"foo\":\"bar\"}\n\nid: 124\nevent: chat\ndata: hi! dude\n\n")
|
||||
}
|
||||
Reference in New Issue
Block a user