github.com/OpsMx/go-app-base@v0.0.24/sse/sse.go (about) 1 // Copyright 2023 OpsMx, Inc 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sse 16 17 import ( 18 "bufio" 19 "fmt" 20 "io" 21 "net/http" 22 "strings" 23 ) 24 25 type SSE struct { 26 scanner *bufio.Scanner 27 autoFlush bool 28 } 29 30 type Event map[string]string 31 32 func NewSSE(r io.Reader) *SSE { 33 scanner := bufio.NewScanner(r) 34 scanner.Split(bufio.ScanLines) 35 return &SSE{ 36 scanner: scanner, 37 autoFlush: true, 38 } 39 } 40 41 // AutoFlush allows changing the auto-flush behavior. Default is to auto-flush after each 42 // event. 43 func (sse *SSE) AutoFlush(af bool) { 44 sse.autoFlush = af 45 } 46 47 // Read will return an event, which may be empty if nothing but a keep-alive was 48 // received thus far. The boolean flag indicates EOF. If true, no more reads should 49 // be performed on this SSE. 50 func (sse *SSE) Read() (Event, bool) { 51 ret := Event{} 52 53 for sse.scanner.Scan() { 54 line := sse.scanner.Text() 55 if line == "" { 56 if len(ret) > 0 { 57 return ret, false 58 } 59 continue 60 } 61 if line == ":" { 62 if len(ret) == 0 { 63 return ret, false 64 } 65 continue 66 } 67 parts := strings.SplitN(line, ":", 2) 68 current := ret[parts[0]] 69 if current != "" { 70 current = current + "\n" 71 } 72 current = current + strings.TrimSpace(parts[1]) 73 ret[parts[0]] = current 74 } 75 return Event{}, true 76 } 77 78 func (sse *SSE) Write(w io.Writer, event Event) error { 79 if len(event) == 0 { 80 return nil 81 } 82 83 for k, v := range event { 84 for _, vv := range strings.Split(v, "\n") { 85 s := k + ": " + vv + "\n" 86 n, err := w.Write([]byte(s)) 87 if err != nil { 88 return err 89 } 90 if n != len(s) { 91 return fmt.Errorf("short write: %d of %d", n, len(s)) 92 } 93 } 94 } 95 n, err := w.Write([]byte("\n")) 96 if err != nil { 97 return err 98 } 99 if n != 1 { 100 return fmt.Errorf("short write: %d of 1", n) 101 } 102 if sse.autoFlush { 103 flusher, ok := w.(http.Flusher) 104 if ok { 105 flusher.Flush() 106 } 107 } 108 return nil 109 } 110 111 func (sse *SSE) KeepAlive(w io.Writer) error { 112 n, err := w.Write([]byte(":\n")) 113 if err != nil { 114 return err 115 } 116 if n != 2 { 117 return fmt.Errorf("short write: %d of 2", n) 118 } 119 return nil 120 121 }