Messaging Service

The PubSub+ Messaging API for Go provides the MessagingService interface, which makes it easy to connect to an event broker. The MessagingService interface handles all the functionality for interacting with a PubSub+ event broker. To create a MessagingService instance, you must first configure a ServicePropertyMap with the information required to establish a connection to the event broker, including the host details and the authentication scheme.

Creating a Property Map

ServicePropertyMap (accessed through the config package which is included in the PubSub+ Go API) can have a number of properties, however the ServicePropertyMap must contain the keys config.TransportLayerPropertyHost and config.ServicePropertyVPNName. The ServicePropertyMap is passed to the MessagingServiceBuilder to configure the connection to the event broker. There are five categories of properties that can be configured in a ServicePropertyMap using the config package:.

  • config.ServiceProperty(required for the VPN property)
  • config.TransportLayerProperty (required for HOST property )
  • config.AuthenticationProperty
  • config.ClientProperty
  • config.TransportLayerSecurityProperty

For more information, see the PubSub+ Messaging API for Go reference.

The code below shows how to use a ServicePropertyMap for establishing a connection to an event broker using basic authentication:

package main

import (
	"fmt"
	"os"
	"os/signal"
	"strconv"
	"time"

	"solace.dev/go/messaging"
	"solace.dev/go/messaging/pkg/solace/config"
	"solace.dev/go/messaging/pkg/solace/resource"
)					
...
...
// Configuration parameters
brokerConfig := config.ServicePropertyMap{
config.TransportLayerPropertyHost:                 "tcps://messaging.solace.cloud:55443",
config.ServicePropertyVPNName:                     "MyVPN",
config.AuthenticationPropertySchemeBasicPassword:  "MyPassword123",
config.AuthenticationPropertySchemeBasicUserName:  "MyUsername",
}

Alternatively if you're running samples, the configuration you want to use can be passed into the client application via a JSON file. The following sample code shows how you can use the UnmarshalJSON() function to parse JSON data and use it to create a ServicePropertyMap:

var configMap config.ServicePropertyMap
data,err := os.ReadFile("/path/to/config.json")  // ReadFile reads the named file and returns the contents.
error := json.Unmarshal(data, &configMap)         // Parses the JSON-encoded data and stores the result in the value pointed to by 2nd paramater.
if error != nil {
panic(error)
}
// Use FromConfigurationProvider(configMap) to use JSON data when creating your MessagingService instance

End-to-End Payload Compression

The PubSub+ Go API can perform end-to-end payload compression to allow for:

  • faster message throughput

  • reduced bandwidth usage

  • improved performance in your applications

While end-to-end payload compression creates more work for individual PubSub+ APIs, it enables faster aggregate rates of message publishing and message receiving. The PubSub+ event broker offers transport message compression that compresses the entire message, see Streaming Compressed Connections. However, when you send and receive large messages, compressing the entire message creates a lot of work for the event broker, which can result in slower throughput. If your application needs to send and receive large messages, we recommend you use end-to-end payload compression to improve performance.

  1. Considerations When Compressing Message Payloads

  2. Compressing Message Payloads in the PubSub+ Go API

Considerations When Compressing Message Payloads

The following sections explain what you should be aware of when you compress message payloads with the PubSub+ Messaging APIs.

Use only one form of message compression

We recommend you only use one form of compression, either Streaming Compressed Connections through the event broker or end-to-end payload compression through the APIs. Compressing the same message multiple times can waste resources and usually does not result in smaller message sizes.

Do not use end-to-end payload compression with small messages

End-to-end Payload Compression works best with messages that are a few megabytes in size. Solace does not recommend using message payload compression with small messages, because end-to-end payload compression can actually increase the size of small messages.

Upgrade your publisher and receiver applications

Receiver applications automatically decompress any compressed message payloads only if the API supports message payload compression and is updated to the minimum supported version. If your receiver application does not support message payload compression, this can cause potential errors or exceptions. Make sure you update your publisher and receiver applications to the minimum supported versions for payload compression. For version support information, see Feature Support in PubSub+ Messaging APIs.

End-to-end payload compression limitations

End-to-end payload compression does not currently support PubSub+ Cache.

End-to-end payload compression is not compatible with:

  • SolCache

  • Non-SMF protocols, such as AMQP, HTTP, Kafka and MQTT

If your applications use any of the above, we recommend you do not use end-to-end payload compression.

Compressing Message Payloads in the PubSub+ Go API

Your publisher application can compress the payload of any message before you publish it. To compress a message payload, you must set ServicePropertyPayloadCompressionLevel, which tells the API you want end-to-end payload compression enabled. The payload compression level property can be set to an integer from 0-9:

  • 0—Payload compression is disabled. This is the default setting.

  • 1 - 9—Payload compression is enabled. 1 is the lowest level of compression with the fastest data throughput, and 9 is the highest level of compression with the slowest data throughput.

Your payload compression level should be adjusted according to your network and performance requirements. The following code snippet shows how to set ServicePropertyPayloadCompressionLevel in a broker properties dictionary:

serviceProps := config.ServicePropertyMap{
    // Set other service properties
    config.ServicePropertyPayloadCompressionLevel: "9", // Payload compression enabled with max compression
}						

Establishing a Connection to an Event Broker

When the necessary properties have been set and stored in a ServicePropertyMap, you can use the messaging package to call NewMessagingServiceBuilder() function. This function returns an instance of MessagingServiceBuilder that you call the Build() function on to return a MessagingService instance. A MessagingService instance allows the API to establish a connection to the event broker. The following is a list of commonly used functions to create the necessary MessagingService instance:

  • messaging.NewMessagingServiceBuilder()
  • WithAuthenticationStrategy(AuthenticationStrategy authenticationProvider)
  • FromConfigurationProvider(provider config.ServicePropertiesConfigurationProvider)
  • Build()

For more information, see the PubSub+ Messaging API for Go reference.

After you create the MessagingService instance, you call the Connect() function on it to connect to the event broker.

The following sample code shows how to create a simple MessagingService instance and connect it to an event broker:

messagingService, err := messaging.NewMessagingServiceBuilder().      // Returns an instance of solace.MessagingServiceBuilder.
FromConfigurationProvider(brokerConfig).                              // An example configuration provider is ServicePropertyMap
WithTransportSecurityStrategy(config.NewTransportSecurityStrategy().  // Configures the resulting messaging service with the specified transport security strategy.
WithCertificateValidation(false, true, "./trust_store", "")).         // With Certificate Validation. Note: Make sure you have a trust_store directory with the .pem file stored in it.
WithAuthenticationStrategy(config.BasicUserNamePasswordAuthentication("myUsername", "myPassword123")). // Configures the resulting messaging service with the specified authentication configuration.
Build()     // Creates and returns the built MessagingService instance based on the provided configuration.				
if err != nil {
	panic(err)
}

if err := messagingService.Connect(); err != nil {   // Connects the messaging service to the event broker. This function blocks until the connection attempt is completed.
	panic(err)
}	

Connecting to a Host Event Broker Through Proxies

You can establish a connection to an event broker within your private network via an HTTP or SOCKS5 proxy server. This allows you to make one firewall exception for the external proxy server, so any clients who authenticate with the proxy server can access your event broker. Proxy servers eliminate the need to make firewall exceptions for each connecting client.

To connect to an event broker through an HTTP or SOCKS protocol version 5 proxy server, the host property config.TransportLayerPropertyHost must include the parameters required for a standard connection to an event broker, but it must also include a ProxyService string:

brokerConfig := config.ServicePropertyMap{
config.TransportLayerPropertyHost:   "[Protocol:]Host[:Port][%ProxyService]",
// ...
}

Where:

ProxyService—The proxy server that is used to connect to event broker. The proxy service string format is specified as:

[ProxyProtocol]://[username:password@]proxyHost[:proxyPort]

Where:

  • ProxyProtocol—The protocol used to communication with the proxy server. The valid values are:
    • socks5—Connect to the server with the SOCKS Protocol Version 5, RFC 1928 (IETF Standards Track Document).
    • httpc—Connect to the server with the HTTP Connect Protocol, RFC 2817 (IETF Standards Track Document).
  • username:password@—If authentication is required for the proxy server, the username and password may be specified before the proxy host.
  • proxyHost—The IP address (or hostname) of the proxy server.
  • proxyPort—The port to connect to for a connection. If the port number is not specified, the default is port 1080 for SOCKS5 and port 3128 default for HTTP Connect.

Examples:

The following examples show how to connect to an event broker through a proxy server.

  • 192.168.160.28%socks5://192.168.1.1—Connects to an event broker at 192.168.160.28 through a SOCKS5 proxy server at 192.168.1.1.
  • 192.168.160.28%httpc://192.168.1.1—Connects to an event broker at 192.168.160.28 through an HTTP-Connect proxy server at 192.168.1.1.
  • tcps:solace.company.com%socks5://User:PassWord@proxy.company.com:13128—Connect to an event broker at solace.company.com using SSL over TCP through a SOCKS5 proxy server at proxy.company.com, port 13128. Authenticate with the proxy server using username User and password PassWord.
  • http://192.168.160.28:44444%httpc://proxy.company.com:11050—Connect to the event broker at 192.168.160.28, port 44444, using HTTP. Connect through the proxy server at proxy.company.com, port 11050.

Using Transport Layer Security

Transport Layer Security (TLS) allows for encrypted authentication and data transmission between the PubSub+ Go API and a PubSub+ event broker. The PubSub+ Go API supports Transport Layer Security versions TLS 1.0, TLS 1.1, and TLS 1.2 . The recommended version to use is the most recent version of TLS. Secure Socket Layer (SSL) protocol, version 3 (SSLv3) is also supported. We don't recommend that you use SSL unless it's required for backwards compatibility.

You can use WithTransportSecurityStrategy() with the config package to configure the TLS connection properties to use, or whether or not to disable certificate validation entirely. When you use TLS, you must always use the secure TCP  protocol (tcps or https) in setting the config.TransportLayerPropertyHost property for your connection, for example:

brokerConfig := config.ServicePropertyMap{
	config.TransportLayerPropertyHost:  "tcps://messaging.solace.cloud:55443",
	config.ServicePropertyVPNName:      "MyVPN",
}

We recommend using certificate validation when configuring your messaging service. The PubSub+ Go API's config package provides the following function for configuring certificate validation:

  • WithCertificateValidation(ignoreExpiration bool, validateServerName bool, trustStoreFilePath string, trustedCommonNameList string)

    • ignoreExpiration— When set to true, expired certificates are accepted.
    • validateServerName— When set to true, certificates without the matching host are not accepted.
    • trustStoreFilePath— The location of the trust store files. If an empty string is passed, no file path will be set.
    • trustedCommonNameList— A comma-separated list of acceptable common names for matching with server certificates. An empty string will match no names.

The following sample code shows the recommended security setup for client applications when you use TLS:

messagingService0, err := messaging.NewMessagingServiceBuilder().
FromConfigurationProvider(brokerConfig).
WithTransportSecurityStrategy(
	config.NewTransportSecurityStrategy().                         // creates a default transport security strategy. Properties can be overwritten by calling configuration functions.
	WithCertificateValidation(false, true, "./trust_store", "").   // Configures TLS validation on certificates. By default, validation is performed.
		WithExcludedProtocols(                                  // Specifies the list of SSL or TLS protocols to not use. 
		config.TransportSecurityProtocolTLSv1,                  // Excludes dated protocols TLSv1, TSLv1_1 and SSLv3.
		config.TransportSecurityProtocolTLSv1_1,
		config.TransportSecurityProtocolSSLv3)).
WithAuthenticationStrategy(config.BasicUserNamePasswordAuthentication("myUserName123", "myPassword123")).
Build()	

There are also a number of functions that you may find useful to configure the TLS connection using the WithTransportSecurityStrategy() function. Here are three functions commonly used to configure the TLS connection:

  • WithCipherSuites()—The list of cipher suites used when negotiating the TLS connection can be configured. You can configure the PubSub+ Go API to limit it to use a set of stronger ciphers and to help guarantee a more secure connection between the client applications and event brokers.

    Cipher suites are a useful method of encrypting communication through a TLS handshake and offer improved security between applications connected through the event broker. For a more detailed explanation and a list of supported cipher suites see Cipher Suites.

    The following code sample shows how to use the WithCipherSuites() function:

    messagingService00, err := messaging.NewMessagingServiceBuilder().
    FromConfigurationProvider(brokerConfig).
    WithTransportSecurityStrategy(
    config.NewTransportSecurityStrategy().
    WithCipherSuites("CipherSuite1, CipherSuite2").   // Comma separated list that configures cipher suites to use.
    WithAuthenticationStrategy(config.ClientCertificateAuthentication("certFile", "keyFile", "keyPassword")).
    Build()	 

  • WithExcludedProtocols()—You may not want to use specific protocols to connect with microservices. For example, you may not want to use legacy protocols. Use this function to specify the Secure Socket Layer (SSL) and Transport Layer Security (TLS) protocols not to use. Here is sample code that shows you how to exclude a specific protocol:

    messagingService00, err := messaging.NewMessagingServiceBuilder().
    FromConfigurationProvider(brokerConfig).
    WithTransportSecurityStrategy(
    config.NewTransportSecurityStrategy().
    WithExcludedProtocols(config.TransportSecurityProtocolTLSv1).   // Specifies the list of SSL or TLS protocols to not use.
    WithAuthenticationStrategy(config.ClientCertificateAuthentication("certFile", "keyFile", "keyPassword")).
    Build()        

  • WithoutCertificateValidation()—This function configures your TLS connection not to validate server certificates.

    Only use this function in development environments. We recommend that you never use this function in production environments because it creates a security vulnerability.

    The following sample code shows how to use the WithoutCertificateValidation() function:

    messagingService, err := messaging.NewMessagingServiceBuilder().
    FromConfigurationProvider(brokerConfig).
    WithTransportSecurityStrategy(
    config.NewTransportSecurityStrategy().
    WithoutCertificateValidation().   // Configures TLS to not validate the server certificate configured on the remote broker.
    WithAuthenticationStrategy(config.ClientCertificateAuthentication("certFile", "keyFile", "keyPassword")).
    Build()        

You can also configure the aspects of the TLS connection using TransportLayerSecurityProperty in the ServicePropertyMap. The TLS connection can be configured using various fields found in config.TransportLayerSecurityProperty. We recommend that you use the default settings (set to true and enabled) to ensure secure connections for the following properties:

  • config.TransportLayerSecurityPropertyCertRejectExpired
  • config.TransportLayerSecurityPropertyCertValidateServername
  • config.TransportLayerSecurityPropertyCertValidated

For more information about the functions and properties, see the PubSub+ Messaging API for Go reference.

Authentication

The PubSub+ Messing API for Go supports a number of authentication schemes (or strategies). that you can choose from. The scheme that you choose may depend on the credentials that the connecting client is required to provide. You can use one of the following authentication schemes:

Basic Authentication

Basic authentication is the default client authentication scheme which allows a client to authenticate with an event broker using a client username and password. To specify basic authentication, create an instance of a MessagingService and specify the following as the parameter for the WithAuthenticationStrategy() function:

  • config.BasicUserNamePasswordAuthentication("myUsername", "myPassword")

For details see the Messaging API for Go reference.

The following sample code shows how to use basic authentication:

messagingService, err := messaging.NewMessagingServiceBuilder().      // Returns an instance of solace.MessagingServiceBuilder.
FromConfigurationProvider(brokerConfig).                              // An example configuration provider is ServicePropertyMap
WithAuthenticationStrategy(config.BasicUserNamePasswordAuthentication("myUsername", "myPassword123")). // Configures the resulting messaging service with the specified authentication configuration.
Build()     // Creates and returns the built MessagingService instance based on the provided configuration.		

Kerberos Authentication

The PubSub+ Go API provides support for Kerberos Authentication. Connecting using this function requires you to load a Kerberos Keytab on the broker (see Managing Event Broker Files) and Kerberos authentication must be configured and enabled for any Message VPNs that Kerberos-authenticated clients connect to.

. Call the WithAuthenticationStrategy() function and pass the following as the parameters:

  • config.KerberosAuthentication(serviceName string) If the service name is not required, an empty string can be passed to the serviceName argument.

For additional details, see the Messaging API for Go reference.

The following sample code shows how to use Kerboros for authentication:

messagingService, err := messaging.NewMessagingServiceBuilder().              // Returns an instance of solace.MessagingServiceBuilder.
FromConfigurationProvider(brokerConfig).				       // An example configuration provider is ServicePropertyMap
WithAuthenticationStrategy(config.KerberosAuthentication("serviceName")).     // Configures the resulting messaging service with the specified authentication configuration.
Build()	   // Creates and returns the built MessagingService instance based on the provided configuration.	

Client Certificate Authentication

To use the Client certificate authentication scheme, the following steps are required:

  • Configure the host event broker to use TLS connections (see Using Transport Layer Security).

  • Your application must connect to the broker using TLS.

  • Enable Client certificate verification on the Message VPN that the application uses to connect.

  • The client-side certificate must be present in a keystore file and configured using the following:

    • config.ClientCertificateAuthentication(certificateFile, keyFile, keyPassword string)

For additional details see the PubSub+ Messaging API for Go reference.

The following sample code shows how to configure client certificate authentication:

messagingService, err := messaging.NewMessagingServiceBuilder().              // Returns an instance of solace.MessagingServiceBuilder.
FromConfigurationProvider(brokerConfig).				       // An example configuration provider is ServicePropertyMap
WithTransportSecurityStrategy(config.NewTransportSecurityStrategy().          // Configures the resulting messaging service with the specified transport security strategy.
WithCertificateValidation(false, true, "./trust_store", "")).                 // With Certificate Validation. Note: Make sure you have a trust_store directory with the .pem file stored in it.).
WithAuthenticationStrategy(config.ClientCertificateAuthentication("certFile", "keyFile", "keyPassword")).  // Configures the resulting messaging service with the specified authentication configuration.
Build()	   // Creates and returns the built MessagingService based on the provided configuration.			

OAuth 2.0 Authentication

OAuth2.0 is an open standard for access delegation and authorization. It is commonly used as a mechanism to grant websites or applications access to users' information on other websites without giving them access to sensitive credentials. The OAuth authentication scheme allows access through the use of tokens issued to third-party clients by an authorization server that provides access to Message VPNs on PubSub+ event brokers. To use OAuth 2.0 authentication, configure the host event broker to use TLS connections (see Using Transport Layer Security) and make sure your application connects to the event broker using TLS. For more information, see OAuth Authentication.

The PubSub+ Go API supports different fields that can be sent to the event broker:

  • accessToken—a String for applications to make requests for data access

    and/or

    idToken—a String for Open ID Connect (OIDC) connections

  • issuerIdentifier—(Optional) a String to identify the appropriate OAuth profile configuration

OAuth authentication requires an accessToken, an idToken, or both to be enabled using this function:

  • config.OAuth2Authentication(accessToken string, oidcIDToken string, issuerIdentifier string)
    • At least one of accessToken or oidcIDToken must be provided. If any of the parameters is not required, an empty string can be passed.

For additional details, refer to the PubSub+ Messaging API for Go reference.

The following sample code shows how to use OAuth authentication with OpenID Connect (OIDC):

/* Configure service access to use a Open ID connect authentication with an ID token and an optional access token. */
messagingService, err := messaging.NewMessagingServiceBuilder().              // Returns an instance of solace.MessagingServiceBuilder.
FromConfigurationProvider(brokerConfig).				       // An example configuration provider is ServicePropertyMap
WithTransportSecurityStrategy(config.NewTransportSecurityStrategy().          // Configures the resulting messaging service with the specified transport security strategy.
WithCertificateValidation(false, true, "./trust_store", "")).                 // With Certificate Validation. Note: Make sure you have a trust_store directory with the .pem file stored in it.).
WithAuthenticationStrategy(config.OAuth2Authentication("accessToken", "oidcIDToken", "")).  // Configures the resulting messaging service with the specified authentication configuration.
Build()	   // Creates and returns the built MessagingService instance based on the provided configuration.		.
/* Configure service access to use OAuth 2 authentication with an access token and an optional issuer identifier. */
messagingService, err := messaging.NewMessagingServiceBuilder().              // Returns an instance of solace.MessagingServiceBuilder.
FromConfigurationProvider(brokerConfig).				       // An example configuration provider is ServicePropertyMap
WithTransportSecurityStrategy(config.NewTransportSecurityStrategy().          // Configures the resulting messaging service with the specified transport security strategy.
WithCertificateValidation(false, true, "./trust_store", "")).                 // With Certificate Validation. Note: Make sure you have a trust_store directory with the .pem file stored in it.).
WithAuthenticationStrategy(config.OAuth2Authentication("accessToken", "", "issuerIdentifier")).  // Configures the resulting messaging service with the specified authentication configuration.
Build()	   // Creates and returns the built MessagingService instance based on the provided configuration.		

Refreshing Expired OAuth Tokens

By default, event brokers disconnect clients when their tokens expire (see Disconnect on Token Expiration). When a client session is disconnected, the client application tries to reconnect a number of times using the same OAuth token based on the RECONNECTION_ATTEMPTS property. If the connection can't be reestablished due to token expiration, the client application must recreate the session with all its subscriptions.

To update the OAuth token, use the UpdateProperty(property config.ServiceProperty, value interface{}) method, which allows you to set a modifable service property after the creation of the MessagingService instance. The first parameter is one of the following strings and the second parameter is the token:

Modifiable service properties may not update immediately and may require the next reconnection attempt to update.

Refreshing the expired token can happen while:

  • the client application is connected. In this case, the client contacts the authentication server to refresh the token and modifies the session to use the updated token the next time the API connects to the event broker.

  • the client application is reconnecting. The reconnecting event includes a diagnostic subCode. If this subCode is Login Failure, this may indicate that your token has expired. In this case, the API tries to reconnect (using the expired token). The client then contacts the authentication server to refresh the token and modifies the session to use the updated token the next time the API attempts to reconnect to the event broker.

In general, it is better if the client application is aware of potential token expiry and refreshes the token before it expires.

When the client application's session is reconnected, the Go API re-applies the client application's direct subscriptions. If there is a change in the ACLs as a result of the refreshed token, the subscriptions may be rejected by the event broker.