sse-encoder.go 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. package sse
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "reflect"
  7. "strings"
  8. )
  9. // Server-Sent Events
  10. // W3C Working Draft 29 October 2009
  11. // http://www.w3.org/TR/2009/WD-eventsource-20091029/
  12. const ContentType = "text/event-stream"
  13. type Event struct {
  14. Event string
  15. Id string
  16. Retry uint
  17. Data interface{}
  18. }
  19. func Encode(w io.Writer, event Event) error {
  20. writeId(w, event.Id)
  21. writeEvent(w, event.Event)
  22. writeRetry(w, event.Retry)
  23. return writeData(w, event.Data)
  24. }
  25. func writeId(w io.Writer, id string) {
  26. if len(id) > 0 {
  27. w.Write([]byte("id: "))
  28. w.Write([]byte(escape(id)))
  29. w.Write([]byte("\n"))
  30. }
  31. }
  32. func writeEvent(w io.Writer, event string) {
  33. if len(event) > 0 {
  34. w.Write([]byte("event: "))
  35. w.Write([]byte(escape(event)))
  36. w.Write([]byte("\n"))
  37. }
  38. }
  39. func writeRetry(w io.Writer, retry uint) {
  40. if retry > 0 {
  41. fmt.Fprintf(w, "retry: %d\n", retry)
  42. }
  43. }
  44. func writeData(w io.Writer, data interface{}) error {
  45. w.Write([]byte("data: "))
  46. switch typeOfData(data) {
  47. case reflect.Struct, reflect.Slice, reflect.Map:
  48. err := json.NewEncoder(w).Encode(data)
  49. if err != nil {
  50. return err
  51. }
  52. w.Write([]byte("\n"))
  53. default:
  54. text := fmt.Sprint(data)
  55. fmt.Fprint(w, escape(text), "\n\n")
  56. }
  57. return nil
  58. }
  59. func typeOfData(data interface{}) reflect.Kind {
  60. value := reflect.ValueOf(data)
  61. valueType := value.Kind()
  62. if valueType == reflect.Ptr {
  63. valueType = value.Elem().Kind()
  64. }
  65. return valueType
  66. }
  67. func escape(str string) string {
  68. // any-char = %x0000-0009 / %x000B-000C / %x000E-10FFFF
  69. // ; a Unicode character other than U+000A LINE FEED (LF) or U+000D CARRIAGE RETURN (CR)
  70. str = strings.Replace(str, "\n", "\\n", -1)
  71. str = strings.Replace(str, "\r", "\\r", -1)
  72. return str
  73. }