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 }