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 }