github.com/vmware/transport-go@v1.3.4/bridge/broker_connector.go (about)

     1  // Copyright 2019-2020 VMware, Inc.
     2  // SPDX-License-Identifier: BSD-2-Clause
     3  
     4  package bridge
     5  
     6  import (
     7  	"crypto/tls"
     8  	"fmt"
     9  	"github.com/go-stomp/stomp/v3"
    10  	"github.com/google/uuid"
    11  	"net/url"
    12  	"sync"
    13  )
    14  
    15  // BrokerConnector is used to connect to a message broker over TCP or WebSocket.
    16  type BrokerConnector interface {
    17  	Connect(config *BrokerConnectorConfig, enableLogging bool) (Connection, error)
    18  }
    19  
    20  type brokerConnector struct {
    21  	c         Connection
    22  	config    *BrokerConnectorConfig
    23  	connected bool
    24  }
    25  
    26  // Create a new broker connector
    27  func NewBrokerConnector() BrokerConnector {
    28  	return &brokerConnector{connected: false}
    29  }
    30  
    31  func checkConfig(config *BrokerConnectorConfig) error {
    32  	if config == nil {
    33  		return fmt.Errorf("config is nil")
    34  	}
    35  	if config.ServerAddr == "" {
    36  		return fmt.Errorf("config invalid, config missing server address")
    37  	}
    38  	if config.Username == "" {
    39  		return fmt.Errorf("config invalid, config missing username")
    40  	}
    41  	if config.Password == "" {
    42  		return fmt.Errorf("config invalid, config missing password")
    43  	}
    44  
    45  	// if TLS is being used and no default values are passed, use defaults, so we don't add
    46  	// cognitive load to using the client with just the basics.
    47  	if config.WebSocketConfig != nil && config.WebSocketConfig.UseTLS && config.WebSocketConfig.TLSConfig == nil {
    48  		var basicTLSConfig = &tls.Config{
    49  			InsecureSkipVerify: true,
    50  			MinVersion:         tls.VersionTLS12,
    51  			CipherSuites: []uint16{
    52  				tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
    53  				tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
    54  				tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
    55  				tls.TLS_RSA_WITH_AES_256_CBC_SHA,
    56  			},
    57  		}
    58  		config.WebSocketConfig.TLSConfig = basicTLSConfig
    59  	}
    60  	return nil
    61  }
    62  
    63  // Connect to broker using supplied connector config.
    64  func (bc *brokerConnector) Connect(config *BrokerConnectorConfig, enableLogging bool) (Connection, error) {
    65  
    66  	err := checkConfig(config)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	// use different mechanism for WS connections.
    72  	if config.UseWS {
    73  		return bc.connectWs(config, enableLogging)
    74  	}
    75  
    76  	return bc.connectTCP(config, err)
    77  }
    78  
    79  func (bc *brokerConnector) connectTCP(config *BrokerConnectorConfig, err error) (Connection, error) {
    80  	if config.HostHeader == "" {
    81  		config.HostHeader = "/"
    82  	}
    83  	var options = []func(*stomp.Conn) error{
    84  		stomp.ConnOpt.Login(config.Username, config.Password),
    85  		stomp.ConnOpt.Host(config.HostHeader),
    86  		stomp.ConnOpt.HeartBeat(config.HeartBeatOut, config.HeartBeatIn),
    87  	}
    88  
    89  	if config.STOMPHeader != nil {
    90  		for key, value := range config.STOMPHeader {
    91  			options = append(options, stomp.ConnOpt.Header(key, value))
    92  		}
    93  	}
    94  
    95  	conn, err := stomp.Dial("tcp", config.ServerAddr, options...)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	id := uuid.New()
   100  	bcConn := &connection{
   101  		id:             &id,
   102  		conn:           conn,
   103  		subscriptions:  make(map[string]Subscription),
   104  		useWs:          false,
   105  		connLock:       sync.Mutex{},
   106  		disconnectChan: make(chan bool)}
   107  	bc.c = bcConn
   108  	bc.connected = true
   109  	bc.config = config
   110  	return bcConn, nil
   111  }
   112  
   113  func (bc *brokerConnector) connectWs(config *BrokerConnectorConfig, enableLogging bool) (Connection, error) {
   114  	wsScheme := "ws"
   115  	if config.WebSocketConfig.UseTLS {
   116  		wsScheme += "s"
   117  	}
   118  
   119  	u := url.URL{Scheme: wsScheme, Host: config.ServerAddr, Path: config.WebSocketConfig.WSPath}
   120  	c := NewBridgeWsClient(enableLogging)
   121  	err := c.Connect(&u, config)
   122  	if err != nil {
   123  		return nil, fmt.Errorf("cannot connect to host '%s' via path '%s', stopping", config.ServerAddr, config.WebSocketConfig.WSPath)
   124  	}
   125  	id := uuid.New()
   126  	bcConn := &connection{
   127  		id:             &id,
   128  		wsConn:         c,
   129  		subscriptions:  make(map[string]Subscription),
   130  		useWs:          true,
   131  		connLock:       sync.Mutex{},
   132  		disconnectChan: make(chan bool)}
   133  	bc.c = bcConn
   134  	bc.connected = true
   135  	return bcConn, nil
   136  }