| |
| |
| |
|
|
| package common |
|
|
| import ( |
| "fmt" |
| "io" |
| "net/http" |
| "strings" |
| "sync" |
| ) |
|
|
| type stringWriter interface { |
| io.Writer |
| writeString(string) (int, error) |
| } |
|
|
| type stringWrapper struct { |
| io.Writer |
| } |
|
|
| func (w stringWrapper) writeString(str string) (int, error) { |
| return w.Writer.Write([]byte(str)) |
| } |
|
|
| func checkWriter(writer io.Writer) stringWriter { |
| if w, ok := writer.(stringWriter); ok { |
| return w |
| } else { |
| return stringWrapper{writer} |
| } |
| } |
|
|
| |
| |
| |
|
|
| var contentType = []string{"text/event-stream"} |
| var noCache = []string{"no-cache"} |
|
|
| var fieldReplacer = strings.NewReplacer( |
| "\n", "\\n", |
| "\r", "\\r") |
|
|
| var dataReplacer = strings.NewReplacer( |
| "\n", "\n", |
| "\r", "\\r") |
|
|
| type CustomEvent struct { |
| Event string |
| Id string |
| Retry uint |
| Data interface{} |
|
|
| Mutex sync.Mutex |
| } |
|
|
| func encode(writer io.Writer, event CustomEvent) error { |
| w := checkWriter(writer) |
| return writeData(w, event.Data) |
| } |
|
|
| func writeData(w stringWriter, data interface{}) error { |
| dataReplacer.WriteString(w, fmt.Sprint(data)) |
| if strings.HasPrefix(data.(string), "data") { |
| w.writeString("\n\n") |
| } |
| return nil |
| } |
|
|
| func (r CustomEvent) Render(w http.ResponseWriter) error { |
| r.WriteContentType(w) |
| return encode(w, r) |
| } |
|
|
| func (r CustomEvent) WriteContentType(w http.ResponseWriter) { |
| r.Mutex.Lock() |
| defer r.Mutex.Unlock() |
| header := w.Header() |
| header["Content-Type"] = contentType |
|
|
| if _, exist := header["Cache-Control"]; !exist { |
| header["Cache-Control"] = noCache |
| } |
| } |
|
|