github.com/Jeffail/benthos/v3@v3.65.0/lib/input/reader/websocket.go (about)

     1  package reader
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"net/http"
     7  	"net/url"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/Jeffail/benthos/v3/lib/log"
    12  	"github.com/Jeffail/benthos/v3/lib/message"
    13  	"github.com/Jeffail/benthos/v3/lib/metrics"
    14  	"github.com/Jeffail/benthos/v3/lib/types"
    15  	"github.com/Jeffail/benthos/v3/lib/util/http/auth"
    16  	btls "github.com/Jeffail/benthos/v3/lib/util/tls"
    17  	"github.com/gorilla/websocket"
    18  )
    19  
    20  //------------------------------------------------------------------------------
    21  
    22  // WebsocketConfig contains configuration fields for the Websocket input type.
    23  type WebsocketConfig struct {
    24  	URL         string `json:"url" yaml:"url"`
    25  	OpenMsg     string `json:"open_message" yaml:"open_message"`
    26  	auth.Config `json:",inline" yaml:",inline"`
    27  	TLS         btls.Config `json:"tls" yaml:"tls"`
    28  }
    29  
    30  // NewWebsocketConfig creates a new WebsocketConfig with default values.
    31  func NewWebsocketConfig() WebsocketConfig {
    32  	return WebsocketConfig{
    33  		URL:     "ws://localhost:4195/get/ws",
    34  		OpenMsg: "",
    35  		Config:  auth.NewConfig(),
    36  		TLS:     btls.NewConfig(),
    37  	}
    38  }
    39  
    40  //------------------------------------------------------------------------------
    41  
    42  // Websocket is an input type that reads Websocket messages.
    43  type Websocket struct {
    44  	log   log.Modular
    45  	stats metrics.Type
    46  
    47  	lock *sync.Mutex
    48  
    49  	conf    WebsocketConfig
    50  	client  *websocket.Conn
    51  	tlsConf *tls.Config
    52  }
    53  
    54  // NewWebsocket creates a new Websocket input type.
    55  func NewWebsocket(
    56  	conf WebsocketConfig,
    57  	log log.Modular,
    58  	stats metrics.Type,
    59  ) (*Websocket, error) {
    60  	ws := &Websocket{
    61  		log:   log,
    62  		stats: stats,
    63  		lock:  &sync.Mutex{},
    64  		conf:  conf,
    65  	}
    66  	if conf.TLS.Enabled {
    67  		var err error
    68  		if ws.tlsConf, err = conf.TLS.Get(); err != nil {
    69  			return nil, err
    70  		}
    71  	}
    72  	return ws, nil
    73  }
    74  
    75  //------------------------------------------------------------------------------
    76  
    77  func (w *Websocket) getWS() *websocket.Conn {
    78  	w.lock.Lock()
    79  	ws := w.client
    80  	w.lock.Unlock()
    81  	return ws
    82  }
    83  
    84  //------------------------------------------------------------------------------
    85  
    86  // Connect establishes a connection to a Websocket server.
    87  func (w *Websocket) Connect() error {
    88  	return w.ConnectWithContext(context.Background())
    89  }
    90  
    91  // ConnectWithContext establishes a connection to a Websocket server.
    92  func (w *Websocket) ConnectWithContext(ctx context.Context) error {
    93  	w.lock.Lock()
    94  	defer w.lock.Unlock()
    95  
    96  	if w.client != nil {
    97  		return nil
    98  	}
    99  
   100  	headers := http.Header{}
   101  
   102  	purl, err := url.Parse(w.conf.URL)
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	if err := w.conf.Sign(&http.Request{
   108  		URL:    purl,
   109  		Header: headers,
   110  	}); err != nil {
   111  		return err
   112  	}
   113  	var client *websocket.Conn
   114  	if w.conf.TLS.Enabled {
   115  		dialer := websocket.Dialer{
   116  			TLSClientConfig: w.tlsConf,
   117  		}
   118  		if client, _, err = dialer.Dial(w.conf.URL, headers); err != nil {
   119  			return err
   120  
   121  		}
   122  	} else if client, _, err = websocket.DefaultDialer.Dial(w.conf.URL, headers); err != nil {
   123  		return err
   124  	}
   125  
   126  	if len(w.conf.OpenMsg) > 0 {
   127  		if err := client.WriteMessage(
   128  			websocket.BinaryMessage, []byte(w.conf.OpenMsg),
   129  		); err != nil {
   130  			return err
   131  		}
   132  	}
   133  
   134  	w.client = client
   135  	return nil
   136  }
   137  
   138  //------------------------------------------------------------------------------
   139  
   140  // Read attempts to read a new message from the websocket.
   141  func (w *Websocket) Read() (types.Message, error) {
   142  	msg, _, err := w.ReadWithContext(context.Background())
   143  	return msg, err
   144  }
   145  
   146  // ReadWithContext attempts to read a new message from the websocket.
   147  func (w *Websocket) ReadWithContext(ctx context.Context) (types.Message, AsyncAckFn, error) {
   148  	client := w.getWS()
   149  	if client == nil {
   150  		return nil, nil, types.ErrNotConnected
   151  	}
   152  
   153  	_, data, err := client.ReadMessage()
   154  	if err != nil {
   155  		w.lock.Lock()
   156  		w.client = nil
   157  		w.lock.Unlock()
   158  		err = types.ErrNotConnected
   159  		return nil, nil, err
   160  	}
   161  
   162  	return message.New([][]byte{data}), noopAsyncAckFn, nil
   163  }
   164  
   165  // Acknowledge instructs whether the pending messages were propagated
   166  // successfully.
   167  func (w *Websocket) Acknowledge(err error) error {
   168  	return nil
   169  }
   170  
   171  // CloseAsync shuts down the Websocket input and stops reading messages.
   172  func (w *Websocket) CloseAsync() {
   173  	w.lock.Lock()
   174  	if w.client != nil {
   175  		w.client.Close()
   176  		w.client = nil
   177  	}
   178  	w.lock.Unlock()
   179  }
   180  
   181  // WaitForClose blocks until the Websocket input has closed down.
   182  func (w *Websocket) WaitForClose(timeout time.Duration) error {
   183  	return nil
   184  }
   185  
   186  //------------------------------------------------------------------------------