Skip to content

Commit 345ef16

Browse files
authored
MQTT connect configuration (#143)
1 parent e074c5c commit 345ef16

File tree

5 files changed

+177
-24
lines changed

5 files changed

+177
-24
lines changed

Sources/MQTTNIO/AsyncAwaitSupport/MQTTClient+async.swift

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ extension MQTTClient {
2727
/// - queue: Dispatch Queue to run shutdown on
2828
public func shutdown(queue: DispatchQueue = .global()) async throws {
2929
return try await withUnsafeThrowingContinuation { cont in
30-
shutdown(queue: queue) { error in
30+
self.shutdown(queue: queue) { error in
3131
if let error = error {
3232
cont.resume(throwing: error)
3333
} else {
@@ -39,15 +39,16 @@ extension MQTTClient {
3939

4040
/// Connect to MQTT server
4141
///
42-
/// Completes when CONNACK is received
43-
///
44-
/// If `cleanSession` is set to false the Server MUST resume communications with the Client based on state from the current Session (as identified by the Client identifier).
45-
/// If there is no Session associated with the Client identifier the Server MUST create a new Session. The Client and Server MUST store the Session
46-
/// after the Client and Server are disconnected. If set to true then the Client and Server MUST discard any previous Session and start a new one
42+
/// If `cleanSession` is set to false the Server MUST resume communications with the Client based on
43+
/// state from the current Session (as identified by the Client identifier). If there is no Session
44+
/// associated with the Client identifier the Server MUST create a new Session. The Client and Server
45+
/// MUST store the Session after the Client and Server are disconnected. If set to true then the Client
46+
/// and Server MUST discard any previous Session and start a new one
4747
///
4848
/// - Parameters:
4949
/// - cleanSession: should we start with a new session
5050
/// - will: Publish message to be posted as soon as connection is made
51+
/// - Returns: EventLoopFuture to be updated with whether server holds a session for this client
5152
/// - Returns: Whether server held a session for this client and has restored it.
5253
@discardableResult public func connect(
5354
cleanSession: Bool = true,
@@ -56,6 +57,31 @@ extension MQTTClient {
5657
return try await self.connect(cleanSession: cleanSession, will: will).get()
5758
}
5859

60+
/// Connect to MQTT server
61+
///
62+
/// If `cleanSession` is set to false the Server MUST resume communications with the Client based on
63+
/// state from the current Session (as identified by the Client identifier). If there is no Session
64+
/// associated with the Client identifier the Server MUST create a new Session. The Client and Server
65+
/// MUST store the Session after the Client and Server are disconnected. If set to true then the Client
66+
/// and Server MUST discard any previous Session and start a new one
67+
///
68+
/// - Parameters:
69+
/// - cleanSession: should we start with a new session
70+
/// - will: Publish message to be posted as soon as connection is made
71+
/// - Returns: EventLoopFuture to be updated with whether server holds a session for this client
72+
/// - Returns: Whether server held a session for this client and has restored it.
73+
@discardableResult public func connect(
74+
cleanSession: Bool = true,
75+
will: (topicName: String, payload: ByteBuffer, qos: MQTTQoS, retain: Bool)? = nil,
76+
connectConfiguration: ConnectConfiguration
77+
) async throws -> Bool {
78+
return try await self.connect(
79+
cleanSession: cleanSession,
80+
will: will, connectConfiguration:
81+
connectConfiguration
82+
).get()
83+
}
84+
5985
/// Publish message to topic
6086
///
6187
/// Depending on QoS completes when message is sent, when PUBACK is received or when PUBREC

Sources/MQTTNIO/AsyncAwaitSupport/MQTTClientV5+async.swift

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ import NIOCore
1818
extension MQTTClient.V5 {
1919
/// Connect to MQTT server
2020
///
21-
/// If `cleanStart` is set to false the Server MUST resume communications with the Client based on state from the current Session (as identified by the Client identifier).
22-
/// If there is no Session associated with the Client identifier the Server MUST create a new Session. The Client and Server MUST store the Session
23-
/// after the Client and Server are disconnected. If set to true then the Client and Server MUST discard any previous Session and start a new one
21+
/// If `cleanStart` is set to false the Server MUST resume communications with the Client based on
22+
/// state from the current Session (as identified by the Client identifier). If there is no Session
23+
/// associated with the Client identifier the Server MUST create a new Session. The Client and Server
24+
/// MUST store the Session after the Client and Server are disconnected. If set to true then the
25+
/// Client and Server MUST discard any previous Session and start a new one
2426
///
2527
/// The function returns an EventLoopFuture which will be updated with whether the server has restored a session for this client.
2628
///
@@ -29,7 +31,7 @@ extension MQTTClient.V5 {
2931
/// - properties: properties to attach to connect message
3032
/// - will: Publish message to be posted as soon as connection is made
3133
/// - authWorkflow: The authentication workflow. This is currently unimplemented.
32-
/// - Returns: EventLoopFuture to be updated with connack
34+
/// - Returns: CONNACK response
3335
public func connect(
3436
cleanStart: Bool = true,
3537
properties: MQTTProperties = .init(),
@@ -39,6 +41,39 @@ extension MQTTClient.V5 {
3941
return try await self.connect(cleanStart: cleanStart, properties: properties, will: will, authWorkflow: authWorkflow).get()
4042
}
4143

44+
/// Connect to MQTT server
45+
///
46+
/// If `cleanStart` is set to false the Server MUST resume communications with the Client based on
47+
/// state from the current Session (as identified by the Client identifier). If there is no Session
48+
/// associated with the Client identifier the Server MUST create a new Session. The Client and Server
49+
/// MUST store the Session after the Client and Server are disconnected. If set to true then the
50+
/// Client and Server MUST discard any previous Session and start a new one
51+
///
52+
/// The function returns an EventLoopFuture which will be updated with whether the server has restored a session for this client.
53+
///
54+
/// - Parameters:
55+
/// - cleanStart: should we start with a new session
56+
/// - properties: properties to attach to connect message
57+
/// - will: Publish message to be posted as soon as connection is made
58+
/// - authWorkflow: The authentication workflow. This is currently unimplemented.
59+
/// - connectConfiguration: Override client configuration during connection
60+
/// - Returns: CONNACK response
61+
public func connect(
62+
cleanStart: Bool = true,
63+
properties: MQTTProperties = .init(),
64+
will: (topicName: String, payload: ByteBuffer, qos: MQTTQoS, retain: Bool, properties: MQTTProperties)? = nil,
65+
authWorkflow: ((MQTTAuthV5, EventLoop) -> EventLoopFuture<MQTTAuthV5>)? = nil,
66+
connectConfiguration: MQTTClient.ConnectConfiguration
67+
) async throws -> MQTTConnackV5 {
68+
return try await self.connect(
69+
cleanStart: cleanStart,
70+
properties: properties,
71+
will: will,
72+
authWorkflow: authWorkflow,
73+
connectConfiguration: connectConfiguration
74+
).get()
75+
}
76+
4277
/// Publish message to topic
4378
/// - Parameters:
4479
/// - topicName: Topic name on which the message is published

Sources/MQTTNIO/MQTTClient.swift

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,11 @@ public final class MQTTClient {
242242

243243
/// Connect to MQTT server
244244
///
245-
/// If `cleanSession` is set to false the Server MUST resume communications with the Client based on state from the current Session (as identified by the Client identifier).
246-
/// If there is no Session associated with the Client identifier the Server MUST create a new Session. The Client and Server MUST store the Session
247-
/// after the Client and Server are disconnected. If set to true then the Client and Server MUST discard any previous Session and start a new one
245+
/// If `cleanSession` is set to false the Server MUST resume communications with the Client based on
246+
/// state from the current Session (as identified by the Client identifier). If there is no Session
247+
/// associated with the Client identifier the Server MUST create a new Session. The Client and Server
248+
/// MUST store the Session after the Client and Server are disconnected. If set to true then the Client
249+
/// and Server MUST discard any previous Session and start a new one
248250
///
249251
/// The function returns an EventLoopFuture which will be updated with whether the server has restored a session for this client.
250252
///
@@ -255,6 +257,33 @@ public final class MQTTClient {
255257
public func connect(
256258
cleanSession: Bool = true,
257259
will: (topicName: String, payload: ByteBuffer, qos: MQTTQoS, retain: Bool)? = nil
260+
) -> EventLoopFuture<Bool> {
261+
self.connect(
262+
cleanSession: cleanSession,
263+
will: will,
264+
connectConfiguration: .init()
265+
)
266+
}
267+
268+
/// Connect to MQTT server
269+
///
270+
/// If `cleanSession` is set to false the Server MUST resume communications with the Client based on
271+
/// state from the current Session (as identified by the Client identifier). If there is no Session
272+
/// associated with the Client identifier the Server MUST create a new Session. The Client and Server
273+
/// MUST store the Session after the Client and Server are disconnected. If set to true then the Client
274+
/// and Server MUST discard any previous Session and start a new one
275+
///
276+
/// The function returns an EventLoopFuture which will be updated with whether the server has restored a session for this client.
277+
///
278+
/// - Parameters:
279+
/// - cleanSession: should we start with a new session
280+
/// - will: Publish message to be posted as soon as connection is made
281+
/// - connectConfiguration: Override client configuration during connection
282+
/// - Returns: EventLoopFuture to be updated with whether server holds a session for this client
283+
public func connect(
284+
cleanSession: Bool = true,
285+
will: (topicName: String, payload: ByteBuffer, qos: MQTTQoS, retain: Bool)? = nil,
286+
connectConfiguration: ConnectConfiguration
258287
) -> EventLoopFuture<Bool> {
259288
let publish = will.map {
260289
MQTTPublishInfo(
@@ -270,12 +299,13 @@ public final class MQTTClient {
270299
if self.configuration.version == .v5_0, cleanSession == false {
271300
properties.append(.sessionExpiryInterval(0xFFFF_FFFF))
272301
}
302+
let keepAliveInterval = connectConfiguration.keepAliveInterval ?? self.configuration.keepAliveInterval
273303
let packet = MQTTConnectPacket(
274304
cleanSession: cleanSession,
275-
keepAliveSeconds: UInt16(configuration.keepAliveInterval.nanoseconds / 1_000_000_000),
305+
keepAliveSeconds: UInt16(keepAliveInterval.nanoseconds / 1_000_000_000),
276306
clientIdentifier: self.identifier,
277-
userName: self.configuration.userName,
278-
password: self.configuration.password,
307+
userName: connectConfiguration.userName ?? self.configuration.userName,
308+
password: connectConfiguration.password ?? self.configuration.password,
279309
properties: properties,
280310
will: publish
281311
)

Sources/MQTTNIO/MQTTClientV5.swift

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ extension MQTTClient {
2020

2121
/// Connect to MQTT server
2222
///
23-
/// If `cleanStart` is set to false the Server MUST resume communications with the Client based on state from the current Session (as identified by the Client identifier).
24-
/// If there is no Session associated with the Client identifier the Server MUST create a new Session. The Client and Server MUST store the Session
25-
/// after the Client and Server are disconnected. If set to true then the Client and Server MUST discard any previous Session and start a new one
23+
/// If `cleanStart` is set to false the Server MUST resume communications with the Client based on
24+
/// state from the current Session (as identified by the Client identifier). If there is no Session
25+
/// associated with the Client identifier the Server MUST create a new Session. The Client and Server
26+
/// MUST store the Session after the Client and Server are disconnected. If set to true then the
27+
/// Client and Server MUST discard any previous Session and start a new one
2628
///
2729
/// The function returns an EventLoopFuture which will be updated with whether the server has restored a session for this client.
2830
///
@@ -37,6 +39,39 @@ extension MQTTClient {
3739
properties: MQTTProperties = .init(),
3840
will: (topicName: String, payload: ByteBuffer, qos: MQTTQoS, retain: Bool, properties: MQTTProperties)? = nil,
3941
authWorkflow: ((MQTTAuthV5, EventLoop) -> EventLoopFuture<MQTTAuthV5>)? = nil
42+
) -> EventLoopFuture<MQTTConnackV5> {
43+
self.connect(
44+
cleanStart: cleanStart,
45+
properties: properties,
46+
will: will,
47+
authWorkflow: authWorkflow,
48+
connectConfiguration: .init()
49+
)
50+
}
51+
52+
/// Connect to MQTT server
53+
///
54+
/// If `cleanStart` is set to false the Server MUST resume communications with the Client based on
55+
/// state from the current Session (as identified by the Client identifier). If there is no Session
56+
/// associated with the Client identifier the Server MUST create a new Session. The Client and Server
57+
/// MUST store the Session after the Client and Server are disconnected. If set to true then the
58+
/// Client and Server MUST discard any previous Session and start a new one
59+
///
60+
/// The function returns an EventLoopFuture which will be updated with whether the server has restored a session for this client.
61+
///
62+
/// - Parameters:
63+
/// - cleanStart: should we start with a new session
64+
/// - properties: properties to attach to connect message
65+
/// - will: Publish message to be posted as soon as connection is made
66+
/// - authWorkflow: The authentication workflow. This is currently unimplemented.
67+
/// - connectConfiguration: Override client configuration during connection
68+
/// - Returns: EventLoopFuture to be updated with connack
69+
public func connect(
70+
cleanStart: Bool = true,
71+
properties: MQTTProperties = .init(),
72+
will: (topicName: String, payload: ByteBuffer, qos: MQTTQoS, retain: Bool, properties: MQTTProperties)? = nil,
73+
authWorkflow: ((MQTTAuthV5, EventLoop) -> EventLoopFuture<MQTTAuthV5>)? = nil,
74+
connectConfiguration: ConnectConfiguration
4075
) -> EventLoopFuture<MQTTConnackV5> {
4176
let publish = will.map {
4277
MQTTPublishInfo(
@@ -48,17 +83,18 @@ extension MQTTClient {
4883
properties: $0.properties
4984
)
5085
}
86+
let keepAliveInterval = connectConfiguration.keepAliveInterval ?? self.client.configuration.keepAliveInterval
5187
let packet = MQTTConnectPacket(
5288
cleanSession: cleanStart,
53-
keepAliveSeconds: UInt16(client.configuration.keepAliveInterval.nanoseconds / 1_000_000_000),
89+
keepAliveSeconds: UInt16(keepAliveInterval.nanoseconds / 1_000_000_000),
5490
clientIdentifier: self.client.identifier,
55-
userName: self.client.configuration.userName,
56-
password: self.client.configuration.password,
91+
userName: connectConfiguration.userName ?? self.client.configuration.userName,
92+
password: connectConfiguration.password ?? self.client.configuration.password,
5793
properties: properties,
5894
will: publish
5995
)
6096

61-
return self.client.connect(packet: packet).map {
97+
return self.client.connect(packet: packet, authWorkflow: authWorkflow).map {
6298
.init(
6399
sessionPresent: $0.sessionPresent,
64100
reason: MQTTReasonCode(rawValue: $0.returnCode) ?? .unrecognisedReason,
@@ -152,7 +188,7 @@ extension MQTTClient {
152188
return eventLoop.makeSucceededFuture(auth)
153189
}
154190
guard let authWorkflow = authWorkflow else { return eventLoop.makeFailedFuture(MQTTError.authWorkflowRequired) }
155-
return client.processAuth(authPacket, authWorkflow: authWorkflow, on: eventLoop)
191+
return self.client.processAuth(authPacket, authWorkflow: authWorkflow, on: eventLoop)
156192
}
157193
.flatMapThrowing { response -> MQTTAuthV5 in
158194
guard let auth = response as? MQTTAuthPacket else { throw MQTTError.unexpectedMessage }

Sources/MQTTNIO/MQTTConfiguration.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,4 +252,30 @@ extension MQTTClient {
252252
/// WebSocket configuration
253253
public let webSocketConfiguration: WebSocketConfiguration?
254254
}
255+
256+
/// Configuration used at connection time to override values stored in the MQTTClient.Configuration
257+
public struct ConnectConfiguration {
258+
/// MQTT user name.
259+
public let userName: String?
260+
/// MQTT password.
261+
public let password: String?
262+
/// MQTT keep alive period.
263+
public let keepAliveInterval: TimeAmount?
264+
265+
/// Initialize MQTTClient connect configuration struct
266+
///
267+
/// - Parameters:
268+
/// - keepAliveInterval: MQTT keep alive period.
269+
/// - userName: MQTT user name
270+
/// - password: MQTT password
271+
public init(
272+
keepAliveInterval: TimeAmount? = nil,
273+
userName: String? = nil,
274+
password: String? = nil
275+
) {
276+
self.keepAliveInterval = keepAliveInterval
277+
self.userName = userName
278+
self.password = password
279+
}
280+
}
255281
}

0 commit comments

Comments
 (0)