github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/api/controller/pubsub/pubsub.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package pubsub 5 6 import ( 7 "github.com/juju/errors" 8 9 "github.com/juju/juju/api/base" 10 "github.com/juju/juju/rpc/params" 11 ) 12 13 // MessageWriter is the interface that allows sending pub/sub messges to the 14 // server. 15 type MessageWriter interface { 16 // ForwardMessage forwards the given message to the server. 17 ForwardMessage(*params.PubSubMessage) error 18 Close() error 19 } 20 21 // API provides access to the pubsub API. 22 type API struct { 23 connector base.StreamConnector 24 } 25 26 // NewAPI creates a new client-side pubsub API. 27 func NewAPI(connector base.StreamConnector) *API { 28 return &API{connector: connector} 29 } 30 31 // OpenMessageWriter returns a new message writer interface value which must 32 // be closed when finished with. 33 func (api *API) OpenMessageWriter() (MessageWriter, error) { 34 conn, err := api.connector.ConnectStream("/pubsub", nil) 35 if err != nil { 36 return nil, errors.Annotatef(err, "cannot connect to /pubsub") 37 } 38 messageWriter := &writer{conn} 39 go messageWriter.readLoop() 40 return messageWriter, nil 41 } 42 43 type writer struct { 44 conn base.Stream 45 } 46 47 // readLoop is necessary for the client to process websocket control messages. 48 // Close() is safe to call concurrently. 49 func (w *writer) readLoop() { 50 for { 51 if _, _, err := w.conn.NextReader(); err != nil { 52 w.conn.Close() 53 break 54 } 55 } 56 } 57 58 func (w *writer) ForwardMessage(m *params.PubSubMessage) error { 59 // Note: due to the fire-and-forget nature of the 60 // pubsub API, it is possible that when the 61 // connection dies, any messages that were "in-flight" 62 // will not be received on the server side. 63 if err := w.conn.WriteJSON(m); err != nil { 64 return errors.Annotatef(err, "cannot send pubsub message") 65 } 66 return nil 67 } 68 69 func (w *writer) Close() error { 70 return w.conn.Close() 71 }