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  }