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  }