github.com/anycable/anycable-go@v1.5.1/streams/controller.go (about)

     1  package streams
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"log/slog"
     7  
     8  	"github.com/anycable/anycable-go/common"
     9  	"github.com/anycable/anycable-go/node"
    10  	"github.com/anycable/anycable-go/utils"
    11  	"github.com/joomcode/errorx"
    12  )
    13  
    14  type SubscribeRequest struct {
    15  	StreamName       string `json:"stream_name"`
    16  	SignedStreamName string `json:"signed_stream_name"`
    17  
    18  	whisper bool
    19  }
    20  
    21  func (r *SubscribeRequest) IsPresent() bool {
    22  	return r.StreamName != "" || r.SignedStreamName != ""
    23  }
    24  
    25  type StreamResolver = func(string) (*SubscribeRequest, error)
    26  
    27  type Controller struct {
    28  	verifier *utils.MessageVerifier
    29  	resolver StreamResolver
    30  	log      *slog.Logger
    31  }
    32  
    33  var _ node.Controller = (*Controller)(nil)
    34  
    35  func NewController(key string, resolver StreamResolver, l *slog.Logger) *Controller {
    36  	var verifier *utils.MessageVerifier
    37  
    38  	if key != "" {
    39  		verifier = utils.NewMessageVerifier(key)
    40  	}
    41  
    42  	return &Controller{verifier, resolver, l.With("context", "streams")}
    43  }
    44  
    45  func (c *Controller) Start() error {
    46  	return nil
    47  }
    48  
    49  func (c *Controller) Shutdown() error {
    50  	return nil
    51  }
    52  
    53  func (c *Controller) Authenticate(sid string, env *common.SessionEnv) (*common.ConnectResult, error) {
    54  	return nil, nil
    55  }
    56  
    57  func (c *Controller) Subscribe(sid string, env *common.SessionEnv, ids string, identifier string) (*common.CommandResult, error) {
    58  	request, err := c.resolver(identifier)
    59  
    60  	if err != nil {
    61  		return &common.CommandResult{
    62  			Status:        common.FAILURE,
    63  			Transmissions: []string{common.RejectionMessage(identifier)},
    64  		}, errorx.Decorate(err, "invalid identifier")
    65  	}
    66  
    67  	if !request.IsPresent() {
    68  		err := errors.New("malformed identifier: no stream name or signed stream")
    69  
    70  		return &common.CommandResult{
    71  			Status:        common.FAILURE,
    72  			Transmissions: []string{common.RejectionMessage(identifier)},
    73  		}, err
    74  	}
    75  
    76  	var stream string
    77  
    78  	if request.StreamName != "" {
    79  		stream = request.StreamName
    80  
    81  		c.log.With("identifier", identifier).Debug("unsigned", "stream", stream)
    82  	} else {
    83  		verified, err := c.verifier.Verified(request.SignedStreamName)
    84  
    85  		if err != nil {
    86  			c.log.With("identifier", identifier).Debug("verification failed", "stream", request.SignedStreamName, "error", err)
    87  
    88  			return &common.CommandResult{
    89  					Status:        common.FAILURE,
    90  					Transmissions: []string{common.RejectionMessage(identifier)},
    91  				},
    92  				nil
    93  		}
    94  
    95  		var ok bool
    96  		stream, ok = verified.(string)
    97  
    98  		if !ok {
    99  			c.log.With("identifier", identifier).Debug("verification failed: stream name is not a string", "stream", verified)
   100  
   101  			return &common.CommandResult{
   102  					Status:        common.FAILURE,
   103  					Transmissions: []string{common.RejectionMessage(identifier)},
   104  				},
   105  				nil
   106  		}
   107  
   108  		c.log.With("identifier", identifier).Debug("verified", "stream", stream)
   109  	}
   110  
   111  	var state map[string]string
   112  
   113  	if request.whisper {
   114  		state = map[string]string{common.WHISPER_STREAM_STATE: stream}
   115  	}
   116  
   117  	return &common.CommandResult{
   118  		Status:             common.SUCCESS,
   119  		Transmissions:      []string{common.ConfirmationMessage(identifier)},
   120  		Streams:            []string{stream},
   121  		DisconnectInterest: -1,
   122  		IState:             state,
   123  	}, nil
   124  }
   125  
   126  func (c *Controller) Unsubscribe(sid string, env *common.SessionEnv, ids string, identifier string) (*common.CommandResult, error) {
   127  	return &common.CommandResult{
   128  		Status:         common.SUCCESS,
   129  		Transmissions:  []string{},
   130  		Streams:        []string{},
   131  		StopAllStreams: true,
   132  	}, nil
   133  }
   134  
   135  func (c *Controller) Perform(sid string, env *common.SessionEnv, ids string, identifier string, data string) (*common.CommandResult, error) {
   136  	return nil, nil
   137  }
   138  
   139  func (c *Controller) Disconnect(sid string, env *common.SessionEnv, ids string, subscriptions []string) error {
   140  	return nil
   141  }
   142  
   143  func NewStreamsController(conf *Config, l *slog.Logger) *Controller {
   144  	key := conf.Secret
   145  	allowPublic := conf.Public
   146  	whispers := conf.Whisper
   147  
   148  	resolver := func(identifier string) (*SubscribeRequest, error) {
   149  		var request SubscribeRequest
   150  
   151  		if err := json.Unmarshal([]byte(identifier), &request); err != nil {
   152  			return nil, err
   153  		}
   154  
   155  		if !allowPublic && request.StreamName != "" {
   156  			return nil, errors.New("public streams are not allowed")
   157  		}
   158  
   159  		if whispers || (request.StreamName != "") {
   160  			request.whisper = true
   161  		}
   162  
   163  		return &request, nil
   164  	}
   165  
   166  	return NewController(key, resolver, l)
   167  }
   168  
   169  type TurboMessage struct {
   170  	SignedStreamName string `json:"signed_stream_name"`
   171  }
   172  
   173  func NewTurboController(key string, l *slog.Logger) *Controller {
   174  	resolver := func(identifier string) (*SubscribeRequest, error) {
   175  		var msg TurboMessage
   176  
   177  		if err := json.Unmarshal([]byte(identifier), &msg); err != nil {
   178  			return nil, err
   179  		}
   180  
   181  		return &SubscribeRequest{SignedStreamName: msg.SignedStreamName}, nil
   182  	}
   183  
   184  	return NewController(key, resolver, l)
   185  }
   186  
   187  type CableReadyMesssage struct {
   188  	Identifier string `json:"identifier"`
   189  }
   190  
   191  func NewCableReadyController(key string, l *slog.Logger) *Controller {
   192  	resolver := func(identifier string) (*SubscribeRequest, error) {
   193  		var msg CableReadyMesssage
   194  
   195  		if err := json.Unmarshal([]byte(identifier), &msg); err != nil {
   196  			return nil, err
   197  		}
   198  
   199  		return &SubscribeRequest{SignedStreamName: msg.Identifier}, nil
   200  	}
   201  
   202  	return NewController(key, resolver, l)
   203  }