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 //------------------------------------------------------------------------------