Sources/AwsIotDeviceSdkSwift/mqtt5ClientBuilder.swift (513 lines of code) (raw):
/// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
/// SPDX-License-Identifier: Apache-2.0.
@_exported import AwsCommonRuntimeKit
import Foundation
// Helper function to append parameters to username
private func appendToUsernameParameter(
inputString: String, parameterValue: String, parameterPretext: String
) -> String {
var returnString = inputString
if returnString.contains("?") {
returnString += "&"
} else {
returnString += "?"
}
if parameterValue.contains(parameterPretext) {
return returnString + parameterValue
} else {
return returnString + parameterPretext + parameterValue
}
}
/// A utility class used to build an MQTT5 Client configured for use with AWS IoT Core.
public class Mqtt5ClientBuilder {
private var _endpoint: String?
private var _port: UInt32 = 8883
private var _onWebsocketTransform: OnWebSocketHandshakeIntercept?
private var _clientId: String?
private var _username: String?
private var _authUsername: String?
private var _authorizerName: String?
private var _authorizerSiganture: String?
private var _authTokenKeyName: String?
private var _authTokenValue: String?
private var _password: Data?
private var _keepAliveInterval: TimeInterval = 1200
private var _sessionExpiryInterval: TimeInterval?
private var _extendedValidationAndFlowControlOptions: ExtendedValidationAndFlowControlOptions? =
.awsIotCoreDefaults
private var _onPublishReceived: OnPublishReceived?
private var _onLifecycleEventAttemptingConnect: OnLifecycleEventAttemptingConnect?
private var _onLifecycleEventConnectionSuccess: OnLifecycleEventConnectionSuccess?
private var _onLifecycleEventConnectionFailure: OnLifecycleEventConnectionFailure?
private var _onLifecycleEventDisconnection: OnLifecycleEventDisconnection?
private var _onLifecycleEventStopped: OnLifecycleEventStopped?
private var _enableMetricsCollection: Bool = true
private var _tlsOptions: TLSContextOptions?
private var _caPath: String?
private var _caFile: String?
private var _caData: Data?
private var _caDirPath: String?
private var _certLabel: String?
private var _keyLabel: String?
private var _ackTimeout: TimeInterval?
private var _connackTimeout: TimeInterval?
private var _pingTimeout: TimeInterval?
private var _minReconnectDelay: TimeInterval?
private var _maxReconnectDelay: TimeInterval?
private var _minConnectedTimeToResetReconnectDelay: TimeInterval?
private var _retryJitterMode: ExponentialBackoffJitterMode?
private var _clientOperationQueueBehaviorType: ClientOperationQueueBehaviorType?
private var _clientSessionBehaviorType: ClientSessionBehaviorType?
private var _topicAliasingOptions: TopicAliasingOptions?
private var _httpProxyOptions: HTTPProxyOptions?
private var _socketOptions: SocketOptions?
private var _clientBootstrap: ClientBootstrap?
private var _requestResponseInformation: Bool?
private var _requestProblemInformation: Bool?
private var _receiveMaximum: UInt16?
private var _maximumPacketSize: UInt32?
private var _willDelayInterval: TimeInterval?
private var _will: PublishPacket?
private var _userProperties: [UserProperty] = []
// mtlsFromPath
init(certPath: String, keyPath: String, endpoint: String) throws {
_tlsOptions = try TLSContextOptions.makeMTLS(
certificatePath: certPath, privateKeyPath: keyPath)
_endpoint = endpoint
_port = 8883
}
// mtlsFromData
init(certData: Data, keyData: Data, endpoint: String) throws {
_tlsOptions = try TLSContextOptions.makeMTLS(
certificateData: certData, privateKeyData: keyData)
_endpoint = endpoint
_port = 8883
}
// mtlsFromPKCS12
init(pkcs12Path: String, pkcs12Password: String, endpoint: String) throws {
_tlsOptions = try TLSContextOptions.makeMTLS(
pkcs12Path: pkcs12Path, password: pkcs12Password)
_endpoint = endpoint
_port = 8883
}
// websocketsWithDefaultAwsSigning
init(
endpoint: String, region: String, credentialsProvider: CredentialsProvider,
bootstrap: ClientBootstrap? = nil
) throws {
_tlsOptions = TLSContextOptions.makeDefault()
_endpoint = endpoint
_port = 443
_clientBootstrap = bootstrap
let signingConfig = SigningConfig(
algorithm: SigningAlgorithmType.signingV4,
signatureType: SignatureType.requestQueryParams,
service: "iotdevicegateway",
region: region,
credentialsProvider: credentialsProvider,
omitSessionToken: true)
_onWebsocketTransform = { httpRequest, completCallback in
Task {
do {
let returnedHttpRequest = try await Signer.signRequest(
request: httpRequest, config: signingConfig)
completCallback(returnedHttpRequest, 0)
} catch {
completCallback(httpRequest, -1)
}
}
}
}
// Custom Auth
init(
endpoint: String,
authAuthorizerName: String? = nil,
authPassword: Data? = nil,
authAuthorizerSignature: String? = nil,
authTokenKeyName: String? = nil,
authTokenValue: String? = nil,
authUsername: String? = nil,
useWebsocket: Bool = true
) throws {
_endpoint = endpoint
_port = 443
_authorizerName = authAuthorizerName
_password = authPassword
_authorizerSiganture = authAuthorizerSignature
_authTokenKeyName = authTokenKeyName
_authTokenValue = authTokenValue
_authUsername = authUsername
_tlsOptions = TLSContextOptions.makeDefault()
if useWebsocket {
_onWebsocketTransform = { httpRequest, completeCallback in
completeCallback(httpRequest, 0)
}
} else {
_tlsOptions?.setAlpnList(["mqtt"])
}
}
private func buildUsername() {
var usernameString = ""
if let username = _username {
usernameString += username
}
if let authUsernameSet = _authUsername {
usernameString = appendToUsernameParameter(
inputString: usernameString,
parameterValue: authUsernameSet,
parameterPretext: "")
usernameString += authUsernameSet
}
if let authorizerName = _authorizerName {
usernameString = appendToUsernameParameter(
inputString: usernameString,
parameterValue: authorizerName,
parameterPretext: "x-amz-customauthorizer-name=")
}
if let authAuthorizerSignature = _authorizerSiganture {
var encodedSignature = authAuthorizerSignature
if !encodedSignature.contains("%") {
encodedSignature =
encodedSignature.addingPercentEncoding(withAllowedCharacters: .alphanumerics)
?? encodedSignature
}
usernameString = appendToUsernameParameter(
inputString: usernameString,
parameterValue: encodedSignature,
parameterPretext: "x-amz-customauthorizer-signature=")
}
if let tokenKeyName = _authTokenKeyName, let tokenValue = _authTokenValue {
usernameString = appendToUsernameParameter(
inputString: usernameString,
parameterValue: tokenValue,
parameterPretext: "\(tokenKeyName)=")
}
if _enableMetricsCollection {
usernameString = appendToUsernameParameter(
inputString: usernameString,
parameterValue: "SDK=Swift&Version=\(packageVersion)",
parameterPretext: "")
}
if !usernameString.isEmpty {
_username = usernameString
} else {
_username = nil
}
}
/// Create an Mqtt5ClientBuilder configured to connect using certificate and private key file paths.
///
/// - Parameters:
/// - certPath: Path to certificate file.
/// - keyPath: Path to private key file.
/// - endpoint: Host name of AWS IoT server.
/// - Throws: `CommonRuntimeError.crtError`
/// - Returns: An Mqtt5ClientBuilder configured to connect using Mutual TLS.
public static func mtlsFromPath(
certPath: String,
keyPath: String,
endpoint: String
) throws -> Mqtt5ClientBuilder {
return try Mqtt5ClientBuilder(certPath: certPath, keyPath: keyPath, endpoint: endpoint)
}
/// Create an Mqtt5ClientBuilder configured to connect using certificate and private key data.
///
/// - Parameters:
/// - certData: Certificate file bytes.
/// - keyData: Private key bytes.
/// - endpoint: Host name of AWS IoT server.
/// - Throws: `CommonRuntimeError.crtError`
/// - Returns: An Mqtt5ClientBuilder configured to connect using Mutual TLS.
public static func mtlsFromData(
certData: Data,
keyData: Data,
endpoint: String
) throws -> Mqtt5ClientBuilder {
return try Mqtt5ClientBuilder(certData: certData, keyData: keyData, endpoint: endpoint)
}
/// Create an Mqtt5ClientBuilder configured to connect using a PKCS12 file.
///
/// - Parameters:
/// - pkcs12Path: Path to the PKCS12 file to use
/// - pkcs12Password: The password for the PKCS12 file.
/// - endpoint: Host name of AWS IoT server.
/// - Throws: `CommonRuntimeError.crtError`
/// - Returns: An Mqtt5ClientBuilder configured to connect using Mutual TLS.
public static func mtlsFromPKCS12(
pkcs12Path: String,
pkcs12Password: String,
endpoint: String
) throws -> Mqtt5ClientBuilder {
return try Mqtt5ClientBuilder(
pkcs12Path: pkcs12Path, pkcs12Password: pkcs12Password, endpoint: endpoint)
}
public static func websocketsWithDefaultAwsSigning(
endpoint: String,
region: String,
credentialsProvider: CredentialsProvider,
bootstrap: ClientBootstrap? = nil
) throws -> Mqtt5ClientBuilder {
return try Mqtt5ClientBuilder(
endpoint: endpoint,
region: region,
credentialsProvider: credentialsProvider,
bootstrap: bootstrap)
}
public static func websocketsWithCustomAuthorizer(
endpoint: String,
authAuthorizerName: String,
authPassword: Data,
authUsername: String? = nil
) throws -> Mqtt5ClientBuilder {
return try Mqtt5ClientBuilder(
endpoint: endpoint,
authAuthorizerName: authAuthorizerName,
authPassword: authPassword,
authUsername: authUsername,
useWebsocket: true)
}
public static func websocketsWithUnsignedCustomAuthorizer(
endpoint: String,
authAuthorizerName: String,
authPassword: Data? = nil,
authTokenKeyName: String,
authTokenValue: String,
authUsername: String? = nil
) throws -> Mqtt5ClientBuilder {
return try Mqtt5ClientBuilder(
endpoint: endpoint,
authAuthorizerName: authAuthorizerName,
authPassword: authPassword,
authTokenKeyName: authTokenKeyName,
authTokenValue: authTokenValue,
authUsername: authUsername,
useWebsocket: true)
}
public static func websocketsWithSignedCustomAuthorizer(
endpoint: String,
authAuthorizerName: String,
authPassword: Data? = nil,
authAuthorizerSignature: String,
authTokenKeyName: String,
authTokenValue: String,
authUsername: String? = nil
) throws -> Mqtt5ClientBuilder {
return try Mqtt5ClientBuilder(
endpoint: endpoint,
authAuthorizerName: authAuthorizerName,
authPassword: authPassword,
authAuthorizerSignature: authAuthorizerSignature,
authTokenKeyName: authTokenKeyName,
authTokenValue: authTokenValue,
authUsername: authUsername,
useWebsocket: true)
}
public static func directWithUnsignedCustomAuthorizer(
endpoint: String,
authAuthorizerName: String? = nil,
authPassword: Data? = nil,
authUsername: String? = nil
) throws -> Mqtt5ClientBuilder {
return try Mqtt5ClientBuilder(
endpoint: endpoint,
authAuthorizerName: authAuthorizerName,
authPassword: authPassword,
authUsername: authUsername,
useWebsocket: false)
}
public static func directWithSignedCustomAuthorizer(
endpoint: String,
authAuthorizerName: String,
authAuthorizerSignature: String,
authTokenKeyName: String,
authTokenValue: String,
authUsername: String? = nil,
authPassword: Data? = nil
) throws -> Mqtt5ClientBuilder {
return try Mqtt5ClientBuilder(
endpoint: endpoint,
authAuthorizerName: authAuthorizerName,
authPassword: authPassword,
authAuthorizerSignature: authAuthorizerSignature,
authTokenKeyName: authTokenKeyName,
authTokenValue: authTokenValue,
authUsername: authUsername,
useWebsocket: false)
}
/// Set callbacks for MQTT5 Client.
///
/// - Parameters:
/// - onPublishReceived: Callback invoked for all publish packets received by client.
/// - onLifecycleEventAttemptingConnect : Callback invoked for Lifecycle Event Attempting Connect.
/// - onLifecycleEventConnectionSuccess: Callback invoked for Lifecycle Event Connection Success.
/// - onLifecycleEventConnectionFailure: Callback invoked for Lifecycle Event Connection Failure.
/// - onLifecycleEventDisconnection: Callback invoked for Lifecycle Event Disconnection.
/// - onLifecycleEventStopped: Callback invoked for Lifecycle Event Stopped.
public func withCallbacks(
onPublishReceived: OnPublishReceived? = nil,
onLifecycleEventAttemptingConnect: OnLifecycleEventAttemptingConnect? = nil,
onLifecycleEventConnectionSuccess: OnLifecycleEventConnectionSuccess? = nil,
onLifecycleEventConnectionFailure: OnLifecycleEventConnectionFailure? = nil,
onLifecycleEventDisconnection: OnLifecycleEventDisconnection? = nil,
onLifecycleEventStopped: OnLifecycleEventStopped? = nil
) {
withOnPublishReceived(onPublishReceived)
withOnLifecycleEventAttemptingConnect(onLifecycleEventAttemptingConnect)
withOnLifecycleEventConnectionSuccess(onLifecycleEventConnectionSuccess)
withOnLifecycleEventConnectionFailure(onLifecycleEventConnectionFailure)
withOnLifecycleEventDisconnection(onLifecycleEventDisconnection)
withOnLifecycleEventStopped(onLifecycleEventStopped)
}
/// Set callback invoked for all publish packets received by client.
///
/// - Parameter onPublishReceived: Callback invoked for all publish packets received by client.
public func withOnPublishReceived(_ onPublishReceived: OnPublishReceived?) {
_onPublishReceived = onPublishReceived
}
/// Set callback invoked for Lifecycle Event Attempting Connect.
///
/// - Parameter onLifecycleEventAttemptingConnect: Callback invoked for Lifecycle Event Attempting Connect.
public func withOnLifecycleEventAttemptingConnect(
_ onLifecycleEventAttemptingConnect: OnLifecycleEventAttemptingConnect?
) {
_onLifecycleEventAttemptingConnect = onLifecycleEventAttemptingConnect
}
/// Set callback invoked for Lifecycle Event Connection Success.
///
/// - Parameter onLifecycleEventConnectionSuccess: Callback invoked for Lifecycle Event Connection Success.
public func withOnLifecycleEventConnectionSuccess(
_ onLifecycleEventConnectionSuccess: OnLifecycleEventConnectionSuccess?
) {
_onLifecycleEventConnectionSuccess = onLifecycleEventConnectionSuccess
}
/// Set callback invoked for Lifecycle Event Connection Failure.
///
/// - Parameter onLifecycleEventConnectionFailure: Callback invoked for Lifecycle Event Connection Failure.
public func withOnLifecycleEventConnectionFailure(
_ onLifecycleEventConnectionFailure: OnLifecycleEventConnectionFailure?
) {
_onLifecycleEventConnectionFailure = onLifecycleEventConnectionFailure
}
/// Set callback invoked for Lifecycle Event Disconnection.
///
/// - Parameter onLifecycleEventDisconnection: Callback invoked for Lifecycle Event Disconnection.
public func withOnLifecycleEventDisconnection(
_ onLifecycleEventDisconnection: OnLifecycleEventDisconnection?
) {
_onLifecycleEventDisconnection = onLifecycleEventDisconnection
}
/// Set callback invoked for Lifecycle Event Stopped.
///
/// - Parameter onLifecycleEventStopped: Callback invoked for Lifecycle Event Stopped.
public func withOnLifecycleEventStopped(_ onLifecycleEventStopped: OnLifecycleEventStopped?) {
_onLifecycleEventStopped = onLifecycleEventStopped
}
/// **port** (`int`): Override default server port.
/// Default port is 443 if system supports ALPN or websockets are being used.
/// Otherwise, default port is 8883.
///
/// - Parameter port: (UInt32)
public func withPort(_ port: UInt32) {
_port = port
}
/// ID to place in CONNECT packet. Must be unique across all devices/clients.
/// If an ID is already in use, the other client will be disconnected. If one is not provided,
/// AWS IoT server will assign a unique ID for use and return it in the CONNACK packet.
///
/// - Parameter clientId: (String)
public func withClientId(_ clientId: String) {
_clientId = clientId
}
/// Username to connect with.
///
/// - Parameter username: (String)
public func withUsername(_ username: String) {
_username = username
}
/// Password to connect with.
///
/// - Parameter password: (Data)
public func withPassword(_ password: Data) {
_password = password
}
/// The maximum time interval, in seconds, that is permitted to elapse between the point at which the
/// client finishes transmitting one MQTT packet and the point it starts sending the next.
/// The client will use PINGREQ packets to maintain this property. If the responding CONNACK contains
/// a keep alive property value, then that is the negotiated keep alive value. Otherwise, the keep
/// alive sent by the client is the negotiated value. keep_alive_interval_sec must be set to at least
/// 1 second greater than ping_timeout_ms (default 30,000 ms) or it will fail validation.
///
/// - Parameter keepAliveInterval: (TimeInterval)
public func withKeepAliveInterval(_ keepAliveInterval: TimeInterval) {
_keepAliveInterval = keepAliveInterval
}
/// A time interval, in seconds, that the client requests the server to persist this connection's MQTT
/// session state for. Has no meaning if the client has not been configured to rejoin sessions.
/// Must be non-zero in order to successfully rejoin a session. If the responding CONNACK contains
/// a session expiry property value, then that is the negotiated session expiry value. Otherwise,
/// the session expiry sent by the client is the negotiated value.
///
/// - Parameter sessionExpiryInterval: (TimeInterval)
public func withSessionExpiryInterval(_ sessionExpiryInterval: TimeInterval) {
_sessionExpiryInterval = sessionExpiryInterval
}
/// The additional controls for client behavior with respect to operation validation and flow control; these
/// checks go beyond the base MQTT5 spec to respect limits of specific MQTT brokers. If argument is omitted or null,
/// then set to AWS_IOT_CORE_DEFAULTS.
///
/// - Parameter flowControlOptions: (ExtendedValidationAndFlowControlOptions)
public func withExtendedValidationAndFlowControlOptions(
_ flowControlOptions: ExtendedValidationAndFlowControlOptions
) {
_extendedValidationAndFlowControlOptions = flowControlOptions
}
/// Overrides the default system trust store.
///
/// - Parameter caPath: Single file containing all trust CAs in PEM format
public func withCaPath(_ caPath: String) {
_caPath = caPath
}
/// Overrides the default system trust store.
///
/// - Parameter caDirPath: Only used on Unix-style systems where all trust anchors are stored in a directory
/// (e.g. /etc/ssl/certs).
public func withCaDirPath(_ caDirPath: String) {
_caDirPath = caDirPath
}
/// Overrides the default system trust store.
///
/// - Parameter caData: Data containing all trust CAs, in PEM format
public func withCaData(_ caData: Data) {
_caData = caData
}
/// Provide specific human readable labels for the certificate and private key being stored in the
/// Apple keychain. Only used with secitem.
///
/// - Parameters:
/// - certLabel: Human readable label to use with certificate
/// - keyLabel: Human readable label to be used with private key
public func withSecitemLabels(certLabel: String? = nil, keyLabel: String? = nil) {
_certLabel = certLabel
_keyLabel = keyLabel
}
/// Overrides the time interval to wait for an ack after sending a QoS 1+ PUBLISH, SUBSCRIBE, or UNSUBSCRIBE before
/// failing the operation. Defaults to no timeout.
///
/// - Parameter ackTimeout:time interval to wait for an ack after sending a QoS 1+ PUBLISH, SUBSCRIBE,
/// or UNSUBSCRIBE before failing the operation
public func withAckTimeout(_ ackTimeout: TimeInterval) {
_ackTimeout = ackTimeout
}
/// Overrides the time interval to wait after sending a CONNECT request for a CONNACK to arrive. If one does not
/// arrive, the connection will be shut down.
///
/// - Parameter connackTimeout: time interval to wait after sending a CONNECT request for a CONNACK to arrive
public func withConnackTimeout(_ connackTimeout: TimeInterval) {
_connackTimeout = connackTimeout
}
/// Overrides the time interval to wait after sending a PINGREQ for a PINGRESP to arrive. If one does not arrive,
/// the client will close the current connection.
///
/// - Parameter pingTimeout: time interval to wait after sending a PINGREQ for a PINGRESP to arrive
public func withPingTimeout(_ pingTimeout: TimeInterval) {
_pingTimeout = pingTimeout
}
/// Overrides the minimum amount of time to wait to reconnect after a disconnect. Exponential backoff is performed
/// with controllable jitter after each connection failure.
///
/// - Parameter minReconnectDelay: minimum amount of time to wait to reconnect after a disconnect
public func withMinReconnectDelay(_ minReconnectDelay: TimeInterval) {
_minReconnectDelay = minReconnectDelay
}
/// Overrides the maximum amount of time to wait to reconnect after a disconnect. Exponential backoff is performed
/// with controllable jitter after each connection failure.
///
/// - Parameter maxReconnectDelay: maximum amount of time to wait to reconnect after a disconnect
public func withMaxReconnectDelay(_ maxReconnectDelay: TimeInterval) {
_maxReconnectDelay = maxReconnectDelay
}
/// Overrides the amount of time that must elapse with an established connection before the reconnect delay is
/// reset to the minimum. This helps alleviate bandwidth-waste in fast reconnect cycles due to permission
/// failures on operations.
///
/// - Parameter minConnectedTimeToResetReconnectDelay: the amount of time that must elapse with an established
/// connection before the reconnect delay is reset to the minimum
public func withMinConnectedTimeToResetReconnectDelay(
_ minConnectedTimeToResetReconnectDelay: TimeInterval
) {
_minConnectedTimeToResetReconnectDelay = minConnectedTimeToResetReconnectDelay
}
/// Overrides how the reconnect delay is modified in order to smooth out the distribution of reconnection attempt
/// timepoints for a large set of reconnecting clients.
///
/// - Parameter retryJitterMode: controls how the reconnect delay is modified in order to smooth out the distribution of
/// reconnection attempt timepoints for a large set of reconnecting clients.
public func withRetryJitterMode(_ retryJitterMode: ExponentialBackoffJitterMode) {
_retryJitterMode = retryJitterMode
}
/// Overrides how disconnects affect the queued and in-progress operations tracked by the client. Also controls
/// how new operations are handled while the client is not connected. In particular, if the client is not connected,
/// then any operation that would be failed on disconnect (according to these rules) will also be rejected.
///
/// - Parameter clientOperationQueueBehaviorType: how disconnects affect the queued and in-progress operations tracked by the client
public func withClientOperationQueueBehaviorType(
_ clientOperationQueueBehaviorType: ClientOperationQueueBehaviorType
) {
_clientOperationQueueBehaviorType = clientOperationQueueBehaviorType
}
/// Overrides how the MQTT5 client should behave with respect to MQTT sessions.
///
/// - Parameter clientSessionBehaviorType: how the MQTT5 client should behave with respect to MQTT sessions
public func withClientSessionBehaviorType(
_ clientSessionBehaviorType: ClientSessionBehaviorType
) {
_clientSessionBehaviorType = clientSessionBehaviorType
}
/// Overrides how the MQTT5 client should behave with respect to topic aliasing.
///
/// - Parameter topicAliasingOptions: how the MQTT5 client should behave with respect to topic aliasing
public func withTopicAliasingOptions(_ topicAliasingOptions: TopicAliasingOptions) {
_topicAliasingOptions = topicAliasingOptions
}
/// Overrides (tunneling) HTTP proxy usage when establishing MQTT connections.
///
/// - Parameter httpProxyOptions: HTTP proxy options to use when establishing MQTT connections
public func withHttyProxyOptions(_ httpProxyOptions: HTTPProxyOptions) {
_httpProxyOptions = httpProxyOptions
}
/// Overrides the socket properties of the underlying MQTT connections made by the client. Leave undefined to use
/// defaults (no TCP keep alive, 10 second socket timeout).
///
/// - Parameter socketOptions: socket properties of the underlying MQTT connections made by the client
public func withSocketOptions(_ socketOptions: SocketOptions) {
_socketOptions = socketOptions
}
/// Set the client bootstrap used to establish connection.
///
/// - Parameter clientBootstrap: client bootstrap used to establish connection.
public func withBootstrap(_ clientBootstrap: ClientBootstrap) {
_clientBootstrap = clientBootstrap
}
/// If true, requests that the server send response information in the subsequent CONNACK. This response
/// information may be used to set up request-response implementations over MQTT, but doing so is outside
/// the scope of the MQTT5 spec and client.
///
/// - Parameter requestResponseInformation: requests that the server send response information in the subsequent CONNACK
public func withRequestResponseInformation(_ requestResponseInformation: Bool) {
_requestResponseInformation = requestResponseInformation
}
/// If true, requests that the server send additional diagnostic information (via response string or user properties)
/// in DISCONNECT or CONNACK packets from the server.
///
/// - Parameter requestProblemInformation: requests that the server send additional diagnostic information in
/// DISCONNECT or CONNACK packets from the server
public func withRequestProblemInformation(_ requestProblemInformation: Bool) {
_requestProblemInformation = requestProblemInformation
}
/// Notifies the server of the maximum number of in-flight QoS 1 and 2 messages the
/// client is willing to handle. If omitted or null, then no limit is requested.
///
/// - Parameter receiveMaximum: maximum number of in-flight QoS 1 and 2 messages the
/// client is willing to handle
public func withReceiveMaximum(_ receiveMaximum: UInt16) {
_receiveMaximum = receiveMaximum
}
/// Notifies the server of the maximum packet size the client is willing to handle.
/// If omitted or null, then no limit beyond the natural limits of MQTT packet size is requested.
///
/// - Parameter maximumPacketSize: maximum packet size the client is willing to handle
public func withMaximumPacketSize(_ maximumPacketSize: UInt32) {
_maximumPacketSize = maximumPacketSize
}
/// A time interval, in seconds, that the server should wait (for a session reconnection) before sending
/// the will message associated with the connection's session. If omitted, the server will send the will
/// when the associated session is destroyed. If the session is destroyed before a will delay interval has
/// elapsed, then the will must be sent at the time of session destruction.
///
/// - Parameter willDelayInterval: time interval that the server should wait (for a session reconnection)
/// before sending the will message associated with the connection's session
public func withWillDelayInterval(_ willDelayInterval: TimeInterval) {
_willDelayInterval = willDelayInterval
}
/// The definition of a message to be published when the connection's session is destroyed by the server or
/// when the will delay interval has elapsed, whichever comes first. If omitted, then nothing will be sent.
///
/// - Parameter will: the definition of a message to be published when the connection's session is destroyed
/// by the server or when the will delay interval has elapsed
public func withWill(_ will: PublishPacket) {
_will = will
}
/// Array of MQTT5 user properties included with the connect packet.
///
/// - Parameter userProperties: user properties to include with the connect packet
public func withUserProperties(_ userProperties: [UserProperty]) {
_userProperties = userProperties
}
/// Builds an `Mqtt5Client` using the configuration set within.
///
/// - Throws: CommonRuntimeError.crtError
/// - Returns: `Mqtt5Client`
public func build() throws -> Mqtt5Client {
guard let unwrappedEndpoint = _endpoint else {
throw AwsIotDeviceSdkError.missingParameter(
parameterName: "Mqtt5ClientBuilder requires endpoint to build client.")
}
// Builds _username with one set by user, custom auth, and metrics
buildUsername()
_extendedValidationAndFlowControlOptions = .awsIotCoreDefaults
// Configure connection options
let connectOptions = MqttConnectOptions(
keepAliveInterval: _keepAliveInterval,
clientId: _clientId,
username: _username,
password: _password,
sessionExpiryInterval: _sessionExpiryInterval,
requestResponseInformation: _requestResponseInformation,
requestProblemInformation: _requestProblemInformation,
receiveMaximum: _receiveMaximum,
maximumPacketSize: _maximumPacketSize,
willDelayInterval: _willDelayInterval,
will: _will,
userProperties: _userProperties
)
var _tlsCtx: TLSContext?
do {
if let tlsOptions = _tlsOptions {
// Handle CA override
if let caPath = _caPath {
try tlsOptions.overrideDefaultTrustStoreWithPath(caPath: caPath)
} else if let caFile = _caFile {
try tlsOptions.overrideDefaultTrustStoreWithFile(caFile: caFile)
} else if let caData = _caData {
try tlsOptions.overrideDefaultTrustStoreWithData(caData: caData)
}
// Apply labels if available
if _certLabel != nil || _keyLabel != nil {
try tlsOptions.setSecitemLabels(certLabel: _certLabel, keyLabel: _keyLabel)
}
_tlsCtx = try TLSContext(options: tlsOptions, mode: .client)
}
} catch {
throw CommonRunTimeError.crtError(CRTError.makeFromLastError())
}
// Configure client options
let clientOptions = MqttClientOptions(
hostName: unwrappedEndpoint,
port: _port,
bootstrap: _clientBootstrap,
socketOptions: _socketOptions,
tlsCtx: _tlsCtx,
onWebsocketTransform: _onWebsocketTransform,
httpProxyOptions: _httpProxyOptions,
connectOptions: connectOptions,
sessionBehavior: _clientSessionBehaviorType,
extendedValidationAndFlowControlOptions: _extendedValidationAndFlowControlOptions,
offlineQueueBehavior: _clientOperationQueueBehaviorType,
retryJitterMode: _retryJitterMode,
minReconnectDelay: _minReconnectDelay,
maxReconnectDelay: _maxReconnectDelay,
minConnectedTimeToResetReconnectDelay: _minConnectedTimeToResetReconnectDelay,
pingTimeout: _pingTimeout,
connackTimeout: _connackTimeout,
ackTimeout: _ackTimeout,
topicAliasingOptions: _topicAliasingOptions,
onPublishReceivedFn: _onPublishReceived,
onLifecycleEventStoppedFn: _onLifecycleEventStopped,
onLifecycleEventAttemptingConnectFn: _onLifecycleEventAttemptingConnect,
onLifecycleEventConnectionSuccessFn: _onLifecycleEventConnectionSuccess,
onLifecycleEventConnectionFailureFn: _onLifecycleEventConnectionFailure,
onLifecycleEventDisconnectionFn: _onLifecycleEventDisconnection)
// Return the configured Mqtt5Client
return try Mqtt5Client(clientOptions: clientOptions)
}
}