...
1
2
3
4
5 package sse
6
7 import (
8 "encoding/json"
9 "fmt"
10 "io"
11 "net/http"
12 "reflect"
13 "strconv"
14 "strings"
15 )
16
17
18
19
20
21 const ContentType = "text/event-stream"
22
23 var contentType = []string{ContentType}
24 var noCache = []string{"no-cache"}
25
26 var fieldReplacer = strings.NewReplacer(
27 "\n", "\\n",
28 "\r", "\\r")
29
30 var dataReplacer = strings.NewReplacer(
31 "\n", "\ndata:",
32 "\r", "\\r")
33
34 type Event struct {
35 Event string
36 Id string
37 Retry uint
38 Data interface{}
39 }
40
41 func Encode(writer io.Writer, event Event) error {
42 w := checkWriter(writer)
43 writeId(w, event.Id)
44 writeEvent(w, event.Event)
45 writeRetry(w, event.Retry)
46 return writeData(w, event.Data)
47 }
48
49 func writeId(w stringWriter, id string) {
50 if len(id) > 0 {
51 w.WriteString("id:")
52 fieldReplacer.WriteString(w, id)
53 w.WriteString("\n")
54 }
55 }
56
57 func writeEvent(w stringWriter, event string) {
58 if len(event) > 0 {
59 w.WriteString("event:")
60 fieldReplacer.WriteString(w, event)
61 w.WriteString("\n")
62 }
63 }
64
65 func writeRetry(w stringWriter, retry uint) {
66 if retry > 0 {
67 w.WriteString("retry:")
68 w.WriteString(strconv.FormatUint(uint64(retry), 10))
69 w.WriteString("\n")
70 }
71 }
72
73 func writeData(w stringWriter, data interface{}) error {
74 w.WriteString("data:")
75 switch kindOfData(data) {
76 case reflect.Struct, reflect.Slice, reflect.Map:
77 err := json.NewEncoder(w).Encode(data)
78 if err != nil {
79 return err
80 }
81 w.WriteString("\n")
82 default:
83 dataReplacer.WriteString(w, fmt.Sprint(data))
84 w.WriteString("\n\n")
85 }
86 return nil
87 }
88
89 func (r Event) Render(w http.ResponseWriter) error {
90 r.WriteContentType(w)
91 return Encode(w, r)
92 }
93
94 func (r Event) WriteContentType(w http.ResponseWriter) {
95 header := w.Header()
96 header["Content-Type"] = contentType
97
98 if _, exist := header["Cache-Control"]; !exist {
99 header["Cache-Control"] = noCache
100 }
101 }
102
103 func kindOfData(data interface{}) reflect.Kind {
104 value := reflect.ValueOf(data)
105 valueType := value.Kind()
106 if valueType == reflect.Ptr {
107 valueType = value.Elem().Kind()
108 }
109 return valueType
110 }
111
View as plain text