eventstream.go (5049B)
1 package http 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "sync" 8 9 "github.com/aws/smithy-go" 10 smithysync "github.com/aws/smithy-go/sync" 11 ) 12 13 // EventStreamWriter writes events to a stream using a ClientProtocol. 14 // 15 // The writer manages a background goroutine that facilitates the write loop. 16 // Calls to Send() on a writer will block until the message has been written. 17 // 18 // The writer doesn't know anything about signing. If event stream messages are 19 // getting signed by the client then the underlying io.Writer has already been 20 // wrapped to handle that at this point. 21 type EventStreamWriter struct { 22 protocol ClientProtocol 23 schema *smithy.Schema 24 25 eventStream io.WriteCloser 26 stream chan singleflight 27 done chan struct{} 28 err *smithysync.OnceErr 29 30 closeOnce sync.Once 31 } 32 33 // we send one message at a time, the underlying write loop marshals these into 34 // the writer and reports back any error to the error channel 35 type singleflight struct { 36 variant *smithy.Schema 37 event smithy.Serializable 38 errCh chan<- error 39 } 40 41 // NewEventStreamWriter returns an EventStreamWriter for the given schema. 42 func NewEventStreamWriter(protocol ClientProtocol, schema *smithy.Schema, stream io.WriteCloser) *EventStreamWriter { 43 w := &EventStreamWriter{ 44 protocol: protocol, 45 schema: schema, 46 47 eventStream: stream, 48 stream: make(chan singleflight), 49 done: make(chan struct{}), 50 err: smithysync.NewOnceErr(), 51 } 52 53 go w.writeStream() 54 55 return w 56 } 57 58 func (w *EventStreamWriter) writeStream() { 59 defer w.Close() 60 61 for { 62 select { 63 case ev := <-w.stream: 64 err := w.protocol.SerializeEventMessage(w.schema, ev.variant, ev.event, w.eventStream) 65 if err != nil { 66 w.err.SetError(err) 67 } 68 ev.errCh <- err 69 case <-w.done: 70 return 71 } 72 } 73 } 74 75 // Send writes a single event to the stream. 76 func (w *EventStreamWriter) Send(ctx context.Context, variant *smithy.Schema, event smithy.Serializable) error { 77 if err := w.err.Err(); err != nil { 78 return err 79 } 80 81 errCh := make(chan error, 1) 82 select { 83 case w.stream <- singleflight{variant, event, errCh}: 84 case <-ctx.Done(): 85 return ctx.Err() 86 case <-w.done: 87 return fmt.Errorf("stream closed, unable to send event") 88 } 89 90 select { 91 case err := <-errCh: 92 return err 93 case <-ctx.Done(): 94 return ctx.Err() 95 case <-w.done: 96 return fmt.Errorf("stream closed, unable to send event") 97 } 98 } 99 100 // Close signals end-of-stream and closes the underlying writer. Close is 101 // safe for concurrent calls. 102 func (w *EventStreamWriter) Close() error { 103 w.closeOnce.Do(func() { 104 close(w.done) 105 w.err.SetError(w.eventStream.Close()) 106 }) 107 return w.err.Err() 108 } 109 110 // Err returns the first error encountered during writing. 111 func (w *EventStreamWriter) Err() error { 112 return w.err.Err() 113 } 114 115 // ErrorSet returns a channel that is closed when an error occurs. 116 func (w *EventStreamWriter) ErrorSet() <-chan struct{} { 117 return w.err.ErrorSet() 118 } 119 120 // EventStreamReader reads events from a stream using a ClientProtocol. 121 type EventStreamReader struct { 122 protocol ClientProtocol 123 schema *smithy.Schema 124 types *smithy.TypeRegistry 125 126 eventStream io.ReadCloser 127 stream chan smithy.Deserializable 128 done chan struct{} 129 err *smithysync.OnceErr 130 131 closeOnce sync.Once 132 } 133 134 // NewEventStreamReader returns an EventStreamReader that deserializes events 135 // through the given protocol from r. The schema is the event stream union 136 // schema. 137 func NewEventStreamReader(protocol ClientProtocol, schema *smithy.Schema, types *smithy.TypeRegistry, stream io.ReadCloser) *EventStreamReader { 138 r := &EventStreamReader{ 139 protocol: protocol, 140 schema: schema, 141 types: types, 142 143 eventStream: stream, 144 stream: make(chan smithy.Deserializable), 145 done: make(chan struct{}), 146 err: smithysync.NewOnceErr(), 147 } 148 149 go r.readEventStream() 150 151 return r 152 } 153 154 func (r *EventStreamReader) readEventStream() { 155 defer r.Close() 156 defer close(r.stream) 157 158 for { 159 event, err := r.protocol.DeserializeEventMessage(r.schema, r.types, r.eventStream) 160 if err != nil { 161 if err == io.EOF { 162 return 163 } 164 select { 165 case <-r.done: 166 return 167 default: 168 r.err.SetError(err) 169 return 170 } 171 } 172 173 select { 174 case r.stream <- event: 175 case <-r.done: 176 return 177 } 178 } 179 } 180 181 // Events returns the channel from which deserialized events can be read. 182 func (r *EventStreamReader) Events() <-chan smithy.Deserializable { 183 return r.stream 184 } 185 186 // Close stops the reader and releases the underlying stream. Close is safe 187 // for concurrent calls. 188 func (r *EventStreamReader) Close() error { 189 r.closeOnce.Do(func() { 190 close(r.done) 191 r.eventStream.Close() 192 }) 193 return r.err.Err() 194 } 195 196 // Err returns the first error encountered during reading. 197 func (r *EventStreamReader) Err() error { 198 return r.err.Err() 199 } 200 201 // ErrorSet returns a channel that is closed when an error occurs. 202 func (r *EventStreamReader) ErrorSet() <-chan struct{} { 203 return r.err.ErrorSet() 204 } 205 206 // Closed returns a channel that is closed when the reader is closed. 207 func (r *EventStreamReader) Closed() <-chan struct{} { 208 return r.done 209 }