src

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

encode.go (3668B)


      1 package eventstream
      2 
      3 import (
      4 	"bytes"
      5 	"encoding/binary"
      6 	"encoding/hex"
      7 	"encoding/json"
      8 	"fmt"
      9 	"hash"
     10 	"hash/crc32"
     11 	"io"
     12 
     13 	"github.com/aws/smithy-go/logging"
     14 )
     15 
     16 // EncoderOptions is the configuration options for Encoder.
     17 type EncoderOptions struct {
     18 	Logger      logging.Logger
     19 	LogMessages bool
     20 }
     21 
     22 // Encoder provides EventStream message encoding.
     23 type Encoder struct {
     24 	options EncoderOptions
     25 
     26 	headersBuf *bytes.Buffer
     27 	messageBuf *bytes.Buffer
     28 }
     29 
     30 // NewEncoder initializes and returns an Encoder to encode Event Stream
     31 // messages.
     32 func NewEncoder(optFns ...func(*EncoderOptions)) *Encoder {
     33 	o := EncoderOptions{}
     34 
     35 	for _, fn := range optFns {
     36 		fn(&o)
     37 	}
     38 
     39 	return &Encoder{
     40 		options:    o,
     41 		headersBuf: bytes.NewBuffer(nil),
     42 		messageBuf: bytes.NewBuffer(nil),
     43 	}
     44 }
     45 
     46 // Encode encodes a single EventStream message to the io.Writer the Encoder
     47 // was created with. An error is returned if writing the message fails.
     48 func (e *Encoder) Encode(w io.Writer, msg Message) (err error) {
     49 	e.headersBuf.Reset()
     50 	e.messageBuf.Reset()
     51 
     52 	var writer io.Writer = e.messageBuf
     53 	if e.options.Logger != nil && e.options.LogMessages {
     54 		encodeMsgBuf := bytes.NewBuffer(nil)
     55 		writer = io.MultiWriter(writer, encodeMsgBuf)
     56 		defer func() {
     57 			logMessageEncode(e.options.Logger, encodeMsgBuf, msg, err)
     58 		}()
     59 	}
     60 
     61 	if err = EncodeHeaders(e.headersBuf, msg.Headers); err != nil {
     62 		return err
     63 	}
     64 
     65 	crc := crc32.New(crc32IEEETable)
     66 	hashWriter := io.MultiWriter(writer, crc)
     67 
     68 	headersLen := uint32(e.headersBuf.Len())
     69 	payloadLen := uint32(len(msg.Payload))
     70 
     71 	if err = encodePrelude(hashWriter, crc, headersLen, payloadLen); err != nil {
     72 		return err
     73 	}
     74 
     75 	if headersLen > 0 {
     76 		if _, err = io.Copy(hashWriter, e.headersBuf); err != nil {
     77 			return err
     78 		}
     79 	}
     80 
     81 	if payloadLen > 0 {
     82 		if _, err = hashWriter.Write(msg.Payload); err != nil {
     83 			return err
     84 		}
     85 	}
     86 
     87 	msgCRC := crc.Sum32()
     88 	if err := binary.Write(writer, binary.BigEndian, msgCRC); err != nil {
     89 		return err
     90 	}
     91 
     92 	_, err = io.Copy(w, e.messageBuf)
     93 
     94 	return err
     95 }
     96 
     97 func logMessageEncode(logger logging.Logger, msgBuf *bytes.Buffer, msg Message, encodeErr error) {
     98 	w := bytes.NewBuffer(nil)
     99 	defer func() { logger.Logf(logging.Debug, w.String()) }()
    100 
    101 	fmt.Fprintf(w, "Message to encode:\n")
    102 	encoder := json.NewEncoder(w)
    103 	if err := encoder.Encode(msg); err != nil {
    104 		fmt.Fprintf(w, "Failed to get encoded message, %v\n", err)
    105 	}
    106 
    107 	if encodeErr != nil {
    108 		fmt.Fprintf(w, "Encode error: %v\n", encodeErr)
    109 		return
    110 	}
    111 
    112 	fmt.Fprintf(w, "Raw message:\n%s\n", hex.Dump(msgBuf.Bytes()))
    113 }
    114 
    115 func encodePrelude(w io.Writer, crc hash.Hash32, headersLen, payloadLen uint32) error {
    116 	p := messagePrelude{
    117 		Length:     minMsgLen + headersLen + payloadLen,
    118 		HeadersLen: headersLen,
    119 	}
    120 	if err := p.ValidateLens(); err != nil {
    121 		return err
    122 	}
    123 
    124 	err := binaryWriteFields(w, binary.BigEndian,
    125 		p.Length,
    126 		p.HeadersLen,
    127 	)
    128 	if err != nil {
    129 		return err
    130 	}
    131 
    132 	p.PreludeCRC = crc.Sum32()
    133 	err = binary.Write(w, binary.BigEndian, p.PreludeCRC)
    134 	if err != nil {
    135 		return err
    136 	}
    137 
    138 	return nil
    139 }
    140 
    141 // EncodeHeaders writes the header values to the writer encoded in the event
    142 // stream format. Returns an error if a header fails to encode.
    143 func EncodeHeaders(w io.Writer, headers Headers) error {
    144 	for _, h := range headers {
    145 		hn := headerName{
    146 			Len: uint8(len(h.Name)),
    147 		}
    148 		copy(hn.Name[:hn.Len], h.Name)
    149 		if err := hn.encode(w); err != nil {
    150 			return err
    151 		}
    152 
    153 		if err := h.Value.encode(w); err != nil {
    154 			return err
    155 		}
    156 	}
    157 
    158 	return nil
    159 }
    160 
    161 func binaryWriteFields(w io.Writer, order binary.ByteOrder, vs ...any) error {
    162 	for _, v := range vs {
    163 		if err := binary.Write(w, order, v); err != nil {
    164 			return err
    165 		}
    166 	}
    167 	return nil
    168 }