src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

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 }