github.com/anycable/anycable-go@v1.5.1/sse/encoder.go (about)

     1  package sse
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/anycable/anycable-go/common"
     9  	"github.com/anycable/anycable-go/encoders"
    10  	"github.com/anycable/anycable-go/utils"
    11  	"github.com/anycable/anycable-go/ws"
    12  )
    13  
    14  const sseEncoderID = "sse"
    15  
    16  // Tell the client to reconnect in a year in case we don't really want it to re-connect
    17  const retryNoReconnect = int64(31536000000)
    18  
    19  const lastIdDelimeter = "/"
    20  
    21  // Encoder is responsible for converting messages to SSE format (event:, data:, etc.)
    22  // NOTE: It's only used to encode messages from server to client.
    23  type Encoder struct {
    24  	// Whether to send only the "message" field of the payload as data or the whole payload
    25  	UnwrapData bool
    26  }
    27  
    28  func (Encoder) ID() string {
    29  	return sseEncoderID
    30  }
    31  
    32  func (e *Encoder) Encode(msg encoders.EncodedMessage) (*ws.SentFrame, error) {
    33  	msgType := msg.GetType()
    34  
    35  	b, err := json.Marshal(&msg)
    36  	if err != nil {
    37  		panic("Failed to build JSON 😲")
    38  	}
    39  
    40  	var payload string
    41  
    42  	reply, isReply := msg.(*common.Reply)
    43  
    44  	if isReply && reply.Type == "" && e.UnwrapData {
    45  		var data string
    46  
    47  		if replyStr, ok := reply.Message.(string); ok {
    48  			data = replyStr
    49  		} else {
    50  			data = string(utils.ToJSON(reply.Message))
    51  		}
    52  		payload = "data: " + data
    53  	} else {
    54  		payload = "data: " + string(b)
    55  	}
    56  
    57  	if msgType != "" {
    58  		payload = "event: " + msgType + "\n" + payload
    59  	}
    60  
    61  	if reply, ok := msg.(*common.Reply); ok {
    62  		if reply.Offset > 0 && reply.Epoch != "" && reply.StreamID != "" {
    63  			payload += "\nid: " + fmt.Sprintf("%d%s%s%s%s", reply.Offset, lastIdDelimeter, reply.Epoch, lastIdDelimeter, reply.StreamID)
    64  		}
    65  	}
    66  
    67  	if msgType == "disconnect" {
    68  		dmsg, ok := msg.(*common.DisconnectMessage)
    69  		if ok && !dmsg.Reconnect {
    70  			payload += "\nretry: " + fmt.Sprintf("%d", retryNoReconnect)
    71  		}
    72  	}
    73  
    74  	return &ws.SentFrame{FrameType: ws.TextFrame, Payload: []byte(payload)}, nil
    75  }
    76  
    77  func (e Encoder) EncodeTransmission(raw string) (*ws.SentFrame, error) {
    78  	msg := common.Reply{}
    79  
    80  	if err := json.Unmarshal([]byte(raw), &msg); err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	return e.Encode(&msg)
    85  }
    86  
    87  func (Encoder) Decode(raw []byte) (*common.Message, error) {
    88  	return nil, errors.New("unsupported")
    89  }