另客网go项目公用的代码库

sse-decoder.go 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. // Copyright 2014 Manu Martinez-Almeida. All rights reserved.
  2. // Use of this source code is governed by a MIT style
  3. // license that can be found in the LICENSE file.
  4. package sse
  5. import (
  6. "bytes"
  7. "io"
  8. "io/ioutil"
  9. )
  10. type decoder struct {
  11. events []Event
  12. }
  13. func Decode(r io.Reader) ([]Event, error) {
  14. var dec decoder
  15. return dec.decode(r)
  16. }
  17. func (d *decoder) dispatchEvent(event Event, data string) {
  18. dataLength := len(data)
  19. if dataLength > 0 {
  20. //If the data buffer's last character is a U+000A LINE FEED (LF) character, then remove the last character from the data buffer.
  21. data = data[:dataLength-1]
  22. dataLength--
  23. }
  24. if dataLength == 0 && event.Event == "" {
  25. return
  26. }
  27. if event.Event == "" {
  28. event.Event = "message"
  29. }
  30. event.Data = data
  31. d.events = append(d.events, event)
  32. }
  33. func (d *decoder) decode(r io.Reader) ([]Event, error) {
  34. buf, err := ioutil.ReadAll(r)
  35. if err != nil {
  36. return nil, err
  37. }
  38. var currentEvent Event
  39. var dataBuffer *bytes.Buffer = new(bytes.Buffer)
  40. // TODO (and unit tests)
  41. // Lines must be separated by either a U+000D CARRIAGE RETURN U+000A LINE FEED (CRLF) character pair,
  42. // a single U+000A LINE FEED (LF) character,
  43. // or a single U+000D CARRIAGE RETURN (CR) character.
  44. lines := bytes.Split(buf, []byte{'\n'})
  45. for _, line := range lines {
  46. if len(line) == 0 {
  47. // If the line is empty (a blank line). Dispatch the event.
  48. d.dispatchEvent(currentEvent, dataBuffer.String())
  49. // reset current event and data buffer
  50. currentEvent = Event{}
  51. dataBuffer.Reset()
  52. continue
  53. }
  54. if line[0] == byte(':') {
  55. // If the line starts with a U+003A COLON character (:), ignore the line.
  56. continue
  57. }
  58. var field, value []byte
  59. colonIndex := bytes.IndexRune(line, ':')
  60. if colonIndex != -1 {
  61. // If the line contains a U+003A COLON character character (:)
  62. // Collect the characters on the line before the first U+003A COLON character (:),
  63. // and let field be that string.
  64. field = line[:colonIndex]
  65. // Collect the characters on the line after the first U+003A COLON character (:),
  66. // and let value be that string.
  67. value = line[colonIndex+1:]
  68. // If value starts with a single U+0020 SPACE character, remove it from value.
  69. if len(value) > 0 && value[0] == ' ' {
  70. value = value[1:]
  71. }
  72. } else {
  73. // Otherwise, the string is not empty but does not contain a U+003A COLON character character (:)
  74. // Use the whole line as the field name, and the empty string as the field value.
  75. field = line
  76. value = []byte{}
  77. }
  78. // The steps to process the field given a field name and a field value depend on the field name,
  79. // as given in the following list. Field names must be compared literally,
  80. // with no case folding performed.
  81. switch string(field) {
  82. case "event":
  83. // Set the event name buffer to field value.
  84. currentEvent.Event = string(value)
  85. case "id":
  86. // Set the event stream's last event ID to the field value.
  87. currentEvent.Id = string(value)
  88. case "retry":
  89. // If the field value consists of only characters in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9),
  90. // then interpret the field value as an integer in base ten, and set the event stream's reconnection time to that integer.
  91. // Otherwise, ignore the field.
  92. currentEvent.Id = string(value)
  93. case "data":
  94. // Append the field value to the data buffer,
  95. dataBuffer.Write(value)
  96. // then append a single U+000A LINE FEED (LF) character to the data buffer.
  97. dataBuffer.WriteString("\n")
  98. default:
  99. //Otherwise. The field is ignored.
  100. continue
  101. }
  102. }
  103. // Once the end of the file is reached, the user agent must dispatch the event one final time.
  104. d.dispatchEvent(currentEvent, dataBuffer.String())
  105. return d.events, nil
  106. }