github.com/vmware/transport-go@v1.3.4/bridge/broker_connector_test.go (about) 1 // Copyright 2019-2020 VMware, Inc. 2 // SPDX-License-Identifier: BSD-2-Clause 3 4 package bridge 5 6 import ( 7 "bufio" 8 "bytes" 9 "fmt" 10 "log" 11 "net" 12 "net/http" 13 "net/http/httptest" 14 "net/url" 15 "testing" 16 "time" 17 18 "github.com/go-stomp/stomp/v3/frame" 19 "github.com/go-stomp/stomp/v3/server" 20 "github.com/gorilla/websocket" 21 "github.com/stretchr/testify/assert" 22 ) 23 24 var upgrader = websocket.Upgrader{} 25 26 // upgrade http connection to WS and read/write responses. 27 func websocketHandler(w http.ResponseWriter, r *http.Request) { 28 c, err := upgrader.Upgrade(w, r, nil) 29 if err != nil { 30 return 31 } 32 defer c.Close() 33 for { 34 mt, message, err := c.ReadMessage() 35 if err != nil { 36 break 37 } 38 39 br := bytes.NewReader(message) 40 sr := frame.NewReader(br) 41 f, _ := sr.Read() 42 43 var sendFrame *frame.Frame 44 45 switch f.Command { 46 case frame.CONNECT: 47 sendFrame = frame.New(frame.CONNECTED, 48 frame.ContentType, "application/json") 49 50 case frame.SUBSCRIBE: 51 sendFrame = frame.New(frame.MESSAGE, 52 frame.Destination, f.Header.Get(frame.Destination), 53 frame.ContentType, "application/json") 54 sendFrame.Body = []byte("happy baby melody!") 55 56 case frame.UNSUBSCRIBE: 57 sendFrame = frame.New(frame.MESSAGE, 58 frame.Destination, f.Header.Get(frame.Destination), 59 frame.ContentType, "application/json") 60 sendFrame.Body = []byte("bye bye!") 61 } 62 var bb bytes.Buffer 63 bw := bufio.NewWriter(&bb) 64 sw := frame.NewWriter(bw) 65 sw.Write(sendFrame) 66 67 err = c.WriteMessage(mt, bb.Bytes()) 68 if err != nil { 69 break 70 } 71 } 72 } 73 74 //var srv Server 75 var testBrokerAddress = ":51581" 76 var httpServer *httptest.Server 77 var tcpServer net.Listener 78 var webSocketURLChan = make(chan string) 79 var websocketURL string 80 81 func runStompBroker() { 82 l, err := net.Listen("tcp", testBrokerAddress) 83 if err != nil { 84 log.Fatalf("failed to listen: %s", err.Error()) 85 } 86 defer func() { l.Close() }() 87 88 log.Println("TCP listening on", l.Addr().Network(), l.Addr().String()) 89 server.Serve(l) 90 tcpServer = l 91 } 92 93 func runWebSocketEndPoint() { 94 s := httptest.NewServer(http.HandlerFunc(websocketHandler)) 95 log.Println("WebSocket listening on", s.Listener.Addr().Network(), s.Listener.Addr().String()) 96 httpServer = s 97 webSocketURLChan <- s.URL 98 } 99 100 func init() { 101 go runStompBroker() 102 go runWebSocketEndPoint() 103 104 websocketURL = <-webSocketURLChan 105 } 106 107 func TestBrokerConnector_BadConfig(t *testing.T) { 108 tt := []struct { 109 test string 110 config *BrokerConnectorConfig 111 err error 112 }{ 113 { 114 "Missing address from config", 115 &BrokerConnectorConfig{Username: "guest", Password: "guest"}, 116 fmt.Errorf("config invalid, config missing server address")}, 117 { 118 "Missing username from config", 119 &BrokerConnectorConfig{ServerAddr: "somewhere:000"}, 120 fmt.Errorf("config invalid, config missing username")}, 121 { 122 "Missing password from config", 123 &BrokerConnectorConfig{Username: "hi", ServerAddr: "somewhere:000"}, 124 fmt.Errorf("config invalid, config missing password")}, 125 } 126 127 for _, tc := range tt { 128 t.Run(tc.test, func(t *testing.T) { 129 bc := NewBrokerConnector() 130 c, err := bc.Connect(tc.config, true) 131 assert.Nil(t, c) 132 assert.NotNil(t, err) 133 assert.Equal(t, tc.err, err) 134 }) 135 } 136 } 137 138 func TestBrokerConnector_ConnectBroker(t *testing.T) { 139 url, _ := url.Parse(websocketURL) 140 host, port, _ := net.SplitHostPort(url.Host) 141 testHost := host + ":" + port 142 143 tt := []struct { 144 test string 145 config *BrokerConnectorConfig 146 }{ 147 { 148 "Connect via websocket", 149 &BrokerConnectorConfig{ 150 Username: "guest", 151 Password: "guest", 152 WebSocketConfig: &WebSocketConfig{WSPath: "/"}, 153 UseWS: true, 154 ServerAddr: testHost, 155 HttpHeader: map[string][]string{ 156 "Sec-Websocket-Protocol": {"v12.stomp, access-token.something"}, 157 }, 158 STOMPHeader: map[string]string{ 159 "access-token": "token", 160 }, 161 }, 162 }, 163 { 164 "Connect via TCP", 165 &BrokerConnectorConfig{ 166 Username: "guest", 167 Password: "guest", 168 ServerAddr: testBrokerAddress, 169 HeartBeatOut: 30 * time.Second, 170 HeartBeatIn: 30 * time.Second, 171 STOMPHeader: map[string]string{ 172 "access-token": "token", 173 }, 174 }}, 175 } 176 177 for _, tc := range tt { 178 t.Run(tc.test, func(t *testing.T) { 179 180 // connect 181 bc := NewBrokerConnector() 182 c, err := bc.Connect(tc.config, true) 183 184 if err != nil { 185 fmt.Printf("unable to connect, error: %e", err) 186 } 187 188 assert.NotNil(t, c) 189 assert.Nil(t, err) 190 if tc.config.UseWS { 191 assert.NotNil(t, c.(*connection).wsConn) 192 } 193 if !tc.config.UseWS { 194 assert.NotNil(t, c.(*connection).conn) 195 } 196 197 // disconnect 198 err = c.Disconnect() 199 assert.Nil(t, err) 200 if tc.config.UseWS { 201 assert.Nil(t, c.(*connection).wsConn) 202 } 203 if !tc.config.UseWS { 204 assert.Nil(t, c.(*connection).conn) 205 } 206 207 }) 208 } 209 } 210 211 func TestBrokerConnector_ConnectBrokerFail(t *testing.T) { 212 tt := []struct { 213 test string 214 config *BrokerConnectorConfig 215 }{ 216 { 217 "Connect via websocket fails with bad address", 218 &BrokerConnectorConfig{ 219 Username: "guest", 220 Password: "guest", 221 UseWS: true, 222 WebSocketConfig: &WebSocketConfig{WSPath: "/"}, 223 ServerAddr: "nowhere"}}, 224 { 225 "Connect via TCP fails with bad address", 226 &BrokerConnectorConfig{ 227 Username: "guest", Password: "guest", ServerAddr: "somewhere"}}, 228 } 229 230 for _, tc := range tt { 231 t.Run(tc.test, func(t *testing.T) { 232 bc := NewBrokerConnector() 233 c, err := bc.Connect(tc.config, true) 234 assert.Nil(t, c) 235 assert.NotNil(t, err) 236 }) 237 } 238 } 239 240 func TestBrokerConnector_Subscribe(t *testing.T) { 241 url, _ := url.Parse(websocketURL) 242 host, port, _ := net.SplitHostPort(url.Host) 243 testHost := host + ":" + port 244 245 tt := []struct { 246 test string 247 config *BrokerConnectorConfig 248 }{ 249 { 250 "Subscribe via websocket", 251 &BrokerConnectorConfig{ 252 Username: "guest", 253 Password: "guest", 254 UseWS: true, 255 WebSocketConfig: &WebSocketConfig{WSPath: "/"}, 256 ServerAddr: testHost}}, 257 { 258 "Subscribe via TCP", 259 &BrokerConnectorConfig{ 260 Username: "guest", Password: "guest", ServerAddr: testBrokerAddress}}, 261 } 262 263 for _, tc := range tt { 264 t.Run(tc.test, func(t *testing.T) { 265 266 // connect 267 bc := NewBrokerConnector() 268 c, _ := bc.Connect(tc.config, true) 269 s, _ := c.Subscribe("/topic/test") 270 if !tc.config.UseWS { 271 var ping = func() { 272 c.SendMessage("/topic/test", "text/plain", []byte(`happy baby melody!`)) 273 } 274 go ping() 275 } 276 msg := <-s.GetMsgChannel() 277 ba := msg.Payload.([]byte) 278 assert.Equal(t, "happy baby melody!", string(ba)) 279 280 // check re-subscribe returns same sub 281 s2, _ := c.Subscribe("/topic/test") 282 assert.Equal(t, s.GetId().String(), s2.GetId().String()) 283 284 c.Disconnect() 285 }) 286 } 287 } 288 289 func TestBrokerConnector_SubscribeError(t *testing.T) { 290 bc := NewBrokerConnector() 291 c, err := bc.Connect(nil, false) 292 assert.NotNil(t, err) 293 assert.Nil(t, c) 294 295 fakeConn := new(connection) 296 fakeConn.useWs = true 297 298 var s Subscription 299 300 // check websocket connection check 301 s, err = fakeConn.Subscribe("/topic/test") 302 assert.NotNil(t, err) 303 assert.Nil(t, s) 304 305 // test tcp connection check 306 fakeConn = new(connection) 307 s, err = fakeConn.Subscribe("/topic/test") 308 assert.NotNil(t, err) 309 assert.Nil(t, s) 310 } 311 312 func TestBrokerConnector_DisconnectNoConnect(t *testing.T) { 313 var c *connection = nil 314 err := c.Disconnect() 315 assert.NotNil(t, err) 316 assert.Nil(t, c) 317 } 318 319 func TestBrokerConnector_SendMessageOnWs(t *testing.T) { 320 url, _ := url.Parse(websocketURL) 321 host, port, _ := net.SplitHostPort(url.Host) 322 testHost := host + ":" + port 323 324 cf := &BrokerConnectorConfig{ 325 Username: "guest", 326 Password: "guest", 327 UseWS: true, 328 WebSocketConfig: &WebSocketConfig{WSPath: "/"}, 329 ServerAddr: testHost} 330 331 bc := NewBrokerConnector() 332 c, _ := bc.Connect(cf, true) 333 assert.NotNil(t, c) 334 335 e := c.SendMessage("nowhere", "text/plain", []byte("out-there")) 336 assert.Nil(t, e) 337 338 // try and send a message on a closed connection 339 cf = &BrokerConnectorConfig{ 340 Username: "guest", Password: "guest", ServerAddr: testBrokerAddress} 341 342 bc = NewBrokerConnector() 343 c, _ = bc.Connect(cf, true) 344 assert.NotNil(t, c) 345 346 c.Disconnect() 347 348 e = c.SendMessage("nowhere", "text/plain", []byte("out-there")) 349 assert.NotNil(t, e) 350 } 351 352 func TestBrokerConnector_Unsubscribe(t *testing.T) { 353 u := websocketURL 354 url, _ := url.Parse(u) 355 host, port, _ := net.SplitHostPort(url.Host) 356 testHost := host + ":" + port 357 358 tt := []struct { 359 test string 360 config *BrokerConnectorConfig 361 }{ 362 { 363 "Unsubscribe via websocket", 364 &BrokerConnectorConfig{ 365 Username: "guest", 366 Password: "guest", 367 UseWS: true, 368 WebSocketConfig: &WebSocketConfig{WSPath: "/"}, 369 ServerAddr: testHost}}, 370 { 371 "Unsubscribe via TCP", 372 &BrokerConnectorConfig{ 373 Username: "guest", Password: "guest", ServerAddr: testBrokerAddress}}, 374 } 375 376 for _, tc := range tt { 377 t.Run(tc.test, func(t *testing.T) { 378 379 // connect 380 bc := NewBrokerConnector() 381 c, _ := bc.Connect(tc.config, true) 382 s, _ := c.Subscribe("/topic/test") 383 if !tc.config.UseWS { 384 var ping = func() { 385 c.SendMessage("/topic/test", "text/plain", []byte(`my little song`)) 386 } 387 go ping() 388 } 389 390 <-s.GetMsgChannel() 391 392 // unsubscribe 393 err := s.Unsubscribe() 394 395 <-s.GetMsgChannel() // will catch channel close event. 396 assert.Nil(t, err) 397 398 // unsubscribe on sub with no connection 399 x := new(subscription) 400 err = x.Unsubscribe() 401 assert.NotNil(t, err) 402 403 c.Disconnect() 404 }) 405 } 406 }