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