@annadata/capacitor-mqtt-quic 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +399 -0
  2. package/android/NGTCP2_BUILD_INSTRUCTIONS.md +319 -0
  3. package/android/build-nghttp3.sh +182 -0
  4. package/android/build-ngtcp2.sh +289 -0
  5. package/android/build-openssl.sh +302 -0
  6. package/android/build.gradle +75 -0
  7. package/android/gradle.properties +3 -0
  8. package/android/proguard-rules.pro +2 -0
  9. package/android/settings.gradle +1 -0
  10. package/android/src/main/AndroidManifest.xml +1 -0
  11. package/android/src/main/assets/mqttquic_ca.pem +5 -0
  12. package/android/src/main/cpp/CMakeLists.txt +157 -0
  13. package/android/src/main/cpp/ngtcp2_jni.cpp +928 -0
  14. package/android/src/main/kotlin/ai/annadata/mqttquic/MqttQuicPlugin.kt +232 -0
  15. package/android/src/main/kotlin/ai/annadata/mqttquic/client/MQTTClient.kt +339 -0
  16. package/android/src/main/kotlin/ai/annadata/mqttquic/mqtt/MQTT5Properties.kt +250 -0
  17. package/android/src/main/kotlin/ai/annadata/mqttquic/mqtt/MQTT5Protocol.kt +281 -0
  18. package/android/src/main/kotlin/ai/annadata/mqttquic/mqtt/MQTT5ReasonCodes.kt +109 -0
  19. package/android/src/main/kotlin/ai/annadata/mqttquic/mqtt/MQTTProtocol.kt +249 -0
  20. package/android/src/main/kotlin/ai/annadata/mqttquic/mqtt/MQTTTypes.kt +47 -0
  21. package/android/src/main/kotlin/ai/annadata/mqttquic/quic/NGTCP2Client.kt +184 -0
  22. package/android/src/main/kotlin/ai/annadata/mqttquic/quic/QuicClientStub.kt +54 -0
  23. package/android/src/main/kotlin/ai/annadata/mqttquic/quic/QuicTypes.kt +21 -0
  24. package/android/src/main/kotlin/ai/annadata/mqttquic/transport/QUICStreamAdapter.kt +37 -0
  25. package/android/src/main/kotlin/ai/annadata/mqttquic/transport/StreamTransport.kt +91 -0
  26. package/android/src/test/kotlin/ai/annadata/mqttquic/mqtt/MQTTProtocolTest.kt +92 -0
  27. package/dist/esm/definitions.d.ts +66 -0
  28. package/dist/esm/definitions.d.ts.map +1 -0
  29. package/dist/esm/definitions.js +2 -0
  30. package/dist/esm/definitions.js.map +1 -0
  31. package/dist/esm/index.d.ts +5 -0
  32. package/dist/esm/index.d.ts.map +1 -0
  33. package/dist/esm/index.js +7 -0
  34. package/dist/esm/index.js.map +1 -0
  35. package/dist/esm/web.d.ts +28 -0
  36. package/dist/esm/web.d.ts.map +1 -0
  37. package/dist/esm/web.js +183 -0
  38. package/dist/esm/web.js.map +1 -0
  39. package/dist/plugin.cjs.js +217 -0
  40. package/dist/plugin.cjs.js.map +1 -0
  41. package/dist/plugin.js +215 -0
  42. package/dist/plugin.js.map +1 -0
  43. package/ios/MqttQuicPlugin.podspec +34 -0
  44. package/ios/NGTCP2_BUILD_INSTRUCTIONS.md +302 -0
  45. package/ios/Sources/MqttQuicPlugin/Client/MQTTClient.swift +343 -0
  46. package/ios/Sources/MqttQuicPlugin/MQTT/MQTT5Properties.swift +280 -0
  47. package/ios/Sources/MqttQuicPlugin/MQTT/MQTT5Protocol.swift +333 -0
  48. package/ios/Sources/MqttQuicPlugin/MQTT/MQTT5ReasonCodes.swift +113 -0
  49. package/ios/Sources/MqttQuicPlugin/MQTT/MQTTProtocol.swift +322 -0
  50. package/ios/Sources/MqttQuicPlugin/MQTT/MQTTTypes.swift +54 -0
  51. package/ios/Sources/MqttQuicPlugin/MqttQuicPlugin.swift +229 -0
  52. package/ios/Sources/MqttQuicPlugin/QUIC/NGTCP2Bridge.h +29 -0
  53. package/ios/Sources/MqttQuicPlugin/QUIC/NGTCP2Bridge.mm +865 -0
  54. package/ios/Sources/MqttQuicPlugin/QUIC/NGTCP2Client.swift +262 -0
  55. package/ios/Sources/MqttQuicPlugin/QUIC/QuicClientStub.swift +66 -0
  56. package/ios/Sources/MqttQuicPlugin/QUIC/QuicTypes.swift +23 -0
  57. package/ios/Sources/MqttQuicPlugin/Resources/mqttquic_ca.pem +5 -0
  58. package/ios/Sources/MqttQuicPlugin/Transport/QUICStreamAdapter.swift +50 -0
  59. package/ios/Sources/MqttQuicPlugin/Transport/StreamTransport.swift +105 -0
  60. package/ios/Tests/MQTTProtocolTests.swift +82 -0
  61. package/ios/build-nghttp3.sh +173 -0
  62. package/ios/build-ngtcp2.sh +278 -0
  63. package/ios/build-openssl.sh +405 -0
  64. package/package.json +63 -0
@@ -0,0 +1,113 @@
1
+ //
2
+ // MQTT5ReasonCodes.swift
3
+ // MqttQuicPlugin
4
+ //
5
+ // MQTT 5.0 Reason Codes. Matches MQTTD mqttd/reason_codes.py.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ public enum MQTT5ReasonCode: UInt8 {
11
+ // Success
12
+ case success = 0x00
13
+ case normalDisconnection = 0x00
14
+ case disconnectWithWillMessage = 0x04
15
+
16
+ // CONNACK Reason Codes
17
+ case unspecifiedError = 0x80
18
+ case malformedPacket = 0x81
19
+ case protocolError = 0x82
20
+ case implementationSpecificError = 0x83
21
+ case unsupportedProtocolVersion = 0x84
22
+ case clientIdentifierNotValid = 0x85
23
+ case badUserNameOrPassword = 0x86
24
+ case notAuthorized = 0x87
25
+ case serverUnavailable = 0x88
26
+ case serverBusy = 0x89
27
+ case banned = 0x8A
28
+ case badAuthenticationMethod = 0x8C
29
+ case topicNameInvalid = 0x90
30
+ case packetTooLarge = 0x95
31
+ case quotaExceeded = 0x97
32
+ case payloadFormatInvalid = 0x99
33
+ case retainNotSupported = 0x9A
34
+ case qosNotSupported = 0x9B
35
+ case useAnotherServer = 0x9C
36
+ case serverMoved = 0x9D
37
+ case connectionRateExceeded = 0x9F
38
+
39
+ // PUBACK, PUBREC, PUBREL, PUBCOMP Reason Codes
40
+ case noMatchingSubscribers = 0x10
41
+ case unspecifiedErrorPub = 0x80
42
+ case implementationSpecificErrorPub = 0x83
43
+ case notAuthorizedPub = 0x87
44
+ case topicNameInvalidPub = 0x90
45
+ case packetIdentifierInUse = 0x91
46
+ case quotaExceededPub = 0x97
47
+ case payloadFormatInvalidPub = 0x99
48
+
49
+ // SUBACK Reason Codes
50
+ case grantedQoS0 = 0x00
51
+ case grantedQoS1 = 0x01
52
+ case grantedQoS2 = 0x02
53
+ case unspecifiedErrorSub = 0x80
54
+ case implementationSpecificErrorSub = 0x83
55
+ case notAuthorizedSub = 0x87
56
+ case topicFilterInvalid = 0x8F
57
+ case packetIdentifierInUseSub = 0x91
58
+ case quotaExceededSub = 0x97
59
+ case sharedSubscriptionsNotSupported = 0x9E
60
+ case subscriptionIdentifiersNotSupported = 0xA1
61
+ case wildcardSubscriptionsNotSupported = 0xA2
62
+
63
+ // UNSUBACK Reason Codes
64
+ case successUnsub = 0x00
65
+ case noSubscriptionExisted = 0x11
66
+ case unspecifiedErrorUnsub = 0x80
67
+ case implementationSpecificErrorUnsub = 0x83
68
+ case notAuthorizedUnsub = 0x87
69
+ case topicFilterInvalidUnsub = 0x8F
70
+ case packetIdentifierInUseUnsub = 0x91
71
+
72
+ // DISCONNECT Reason Codes
73
+ case normalDisconnectionDisc = 0x00
74
+ case disconnectWithWillMessageDisc = 0x04
75
+ case unspecifiedErrorDisc = 0x80
76
+ case malformedPacketDisc = 0x81
77
+ case protocolErrorDisc = 0x82
78
+ case implementationSpecificErrorDisc = 0x83
79
+ case notAuthorizedDisc = 0x87
80
+ case serverBusyDisc = 0x89
81
+ case serverShuttingDown = 0x8B
82
+ case badAuthenticationMethodDisc = 0x8C
83
+ case keepAliveTimeout = 0x8D
84
+ case sessionTakenOver = 0x8E
85
+ case topicFilterInvalidDisc = 0x8F
86
+ case topicNameInvalidDisc = 0x90
87
+ case receiveMaximumExceeded = 0x93
88
+ case topicAliasInvalid = 0x94
89
+ case packetTooLargeDisc = 0x95
90
+ case messageRateTooHigh = 0x96
91
+ case quotaExceededDisc = 0x97
92
+ case administrativeAction = 0x98
93
+ case payloadFormatInvalidDisc = 0x99
94
+ case retainNotSupportedDisc = 0x9A
95
+ case qosNotSupportedDisc = 0x9B
96
+ case useAnotherServerDisc = 0x9C
97
+ case serverMovedDisc = 0x9D
98
+ case sharedSubscriptionsNotSupportedDisc = 0x9E
99
+ case connectionRateExceededDisc = 0x9F
100
+ case maximumConnectTime = 0xA0
101
+ case subscriptionIdentifiersNotSupportedDisc = 0xA1
102
+ case wildcardSubscriptionsNotSupportedDisc = 0xA2
103
+ }
104
+
105
+ // Compatibility mapping for MQTT 3.1.1 return codes
106
+ public let MQTT3_TO_MQTT5_REASON_CODE: [UInt8: MQTT5ReasonCode] = [
107
+ 0x00: .success,
108
+ 0x01: .unsupportedProtocolVersion,
109
+ 0x02: .clientIdentifierNotValid,
110
+ 0x03: .serverUnavailable,
111
+ 0x04: .badUserNameOrPassword,
112
+ 0x05: .notAuthorized,
113
+ ]
@@ -0,0 +1,322 @@
1
+ //
2
+ // MQTTProtocol.swift
3
+ // MqttQuicPlugin
4
+ //
5
+ // MQTT 3.1.1 encode/decode. Matches MQTTD mqttd/protocol.py.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ public enum MQTTProtocolError: Error {
11
+ case insufficientData(String)
12
+ case invalidRemainingLength(Int)
13
+ case invalidUTF8
14
+ }
15
+
16
+ public final class MQTTProtocol {
17
+
18
+ public static let protocolName = "MQTT"
19
+
20
+ // MARK: - Remaining length
21
+
22
+ /// Encode remaining length (variable-length, 1–4 bytes). Max 268_435_455.
23
+ public static func encodeRemainingLength(_ length: Int) throws -> [UInt8] {
24
+ if length < 0 || length > 268_435_455 {
25
+ throw MQTTProtocolError.invalidRemainingLength(length)
26
+ }
27
+ var enc: [UInt8] = []
28
+ var n = length
29
+ repeat {
30
+ var b = UInt8(n % 128)
31
+ n /= 128
32
+ if n > 0 { b |= 0x80 }
33
+ enc.append(b)
34
+ } while n > 0
35
+ return enc
36
+ }
37
+
38
+ /// Decode remaining length. Returns (length, bytesConsumed).
39
+ public static func decodeRemainingLength(_ data: Data, offset: Int = 0) throws -> (Int, Int) {
40
+ var mul: Int = 1
41
+ var len: Int = 0
42
+ var i = offset
43
+ for _ in 0..<4 {
44
+ if i >= data.count { throw MQTTProtocolError.insufficientData("remaining length") }
45
+ let b = data[i]
46
+ len += Int(b & 0x7F) * mul
47
+ i += 1
48
+ if (b & 0x80) == 0 { break }
49
+ mul *= 128
50
+ }
51
+ return (len, i - offset)
52
+ }
53
+
54
+ // MARK: - String (2-byte length + UTF-8)
55
+
56
+ public static func encodeString(_ s: String) throws -> Data {
57
+ guard let utf8 = s.data(using: .utf8) else { throw MQTTProtocolError.invalidUTF8 }
58
+ let count = utf8.count
59
+ if count > 0xFFFF { throw MQTTProtocolError.insufficientData("string too long") }
60
+ var out = Data(capacity: 2 + count)
61
+ out.append(UInt8((count >> 8) & 0xFF))
62
+ out.append(UInt8(count & 0xFF))
63
+ out.append(utf8)
64
+ return out
65
+ }
66
+
67
+ /// Returns (string, newOffset).
68
+ public static func decodeString(_ data: Data, offset: Int) throws -> (String, Int) {
69
+ if offset + 2 > data.count { throw MQTTProtocolError.insufficientData("string length") }
70
+ let hi = Int(data[offset] & 0xFF)
71
+ let lo = Int(data[offset + 1] & 0xFF)
72
+ let strLen = (hi << 8) | lo
73
+ let start = offset + 2
74
+ if start + strLen > data.count { throw MQTTProtocolError.insufficientData("string content") }
75
+ let sub = data.subdata(in: start..<(start + strLen))
76
+ guard let s = String(data: sub, encoding: .utf8) else { throw MQTTProtocolError.invalidUTF8 }
77
+ return (s, start + strLen)
78
+ }
79
+
80
+ // MARK: - Fixed header
81
+
82
+ /// (messageType, remainingLength, bytesConsumed)
83
+ public static func parseFixedHeader(_ data: Data) throws -> (UInt8, Int, Int) {
84
+ if data.count < 2 { throw MQTTProtocolError.insufficientData("fixed header") }
85
+ let msgType = data[0]
86
+ let (rem, consumed) = try decodeRemainingLength(data, offset: 1)
87
+ return (msgType, rem, 1 + consumed)
88
+ }
89
+
90
+ // MARK: - CONNECT (client → server)
91
+
92
+ public static func buildConnect(
93
+ clientId: String,
94
+ username: String? = nil,
95
+ password: String? = nil,
96
+ keepalive: UInt16 = 60,
97
+ cleanSession: Bool = true
98
+ ) throws -> Data {
99
+ var variableHeader = Data()
100
+ variableHeader.append(try encodeString(Self.protocolName))
101
+ variableHeader.append(MQTTProtocolLevel.v311)
102
+ var flags: UInt8 = 0
103
+ if cleanSession { flags |= MQTTConnectFlags.cleanSession }
104
+ if username != nil { flags |= MQTTConnectFlags.username }
105
+ if password != nil { flags |= MQTTConnectFlags.password }
106
+ variableHeader.append(flags)
107
+ variableHeader.append(UInt8((keepalive >> 8) & 0xFF))
108
+ variableHeader.append(UInt8(keepalive & 0xFF))
109
+
110
+ var payload = Data()
111
+ payload.append(try encodeString(clientId))
112
+ if let u = username { payload.append(try encodeString(u)) }
113
+ if let p = password { payload.append(try encodeString(p)) }
114
+
115
+ let remLen = variableHeader.count + payload.count
116
+ var fixed = Data()
117
+ fixed.append(MQTTMessageType.CONNECT.rawValue)
118
+ fixed.append(contentsOf: try encodeRemainingLength(remLen))
119
+
120
+ var out = Data()
121
+ out.append(fixed)
122
+ out.append(variableHeader)
123
+ out.append(payload)
124
+ return out
125
+ }
126
+
127
+ // MARK: - CONNACK (server → client)
128
+
129
+ public static func buildConnack(returnCode: UInt8 = MQTTConnAckCode.accepted.rawValue) -> Data {
130
+ var out = Data()
131
+ out.append(MQTTMessageType.CONNACK.rawValue)
132
+ out.append(contentsOf: try! encodeRemainingLength(2))
133
+ out.append(0x00) // flags
134
+ out.append(returnCode)
135
+ return out
136
+ }
137
+
138
+ /// Parse CONNACK variable header. Assumes fixed header already consumed.
139
+ /// Returns (sessionPresent, returnCode).
140
+ public static func parseConnack(_ data: Data, offset: Int = 0) throws -> (Bool, UInt8) {
141
+ if offset + 2 > data.count { throw MQTTProtocolError.insufficientData("CONNACK") }
142
+ let flags = data[offset]
143
+ let rc = data[offset + 1]
144
+ return ((flags & 0x01) != 0, rc)
145
+ }
146
+
147
+ // MARK: - PUBLISH
148
+
149
+ public static func buildPublish(
150
+ topic: String,
151
+ payload: Data,
152
+ packetId: UInt16? = nil,
153
+ qos: UInt8 = 0,
154
+ retain: Bool = false
155
+ ) throws -> Data {
156
+ var msgType = MQTTMessageType.PUBLISH.rawValue
157
+ if qos > 0 { msgType |= (qos << 1) }
158
+ if retain { msgType |= 0x01 }
159
+
160
+ var vh = Data()
161
+ vh.append(try encodeString(topic))
162
+ if qos > 0, let pid = packetId {
163
+ vh.append(UInt8((pid >> 8) & 0xFF))
164
+ vh.append(UInt8(pid & 0xFF))
165
+ }
166
+ var pl = vh
167
+ pl.append(payload)
168
+ let remLen = pl.count
169
+ var out = Data()
170
+ out.append(msgType)
171
+ out.append(contentsOf: try encodeRemainingLength(remLen))
172
+ out.append(pl)
173
+ return out
174
+ }
175
+
176
+ /// Parse PUBLISH payload (after fixed header). Assumes fixed header already parsed for QoS.
177
+ /// Returns (topic, packetId?, payload, newOffset). packetId only for QoS > 0.
178
+ public static func parsePublish(_ data: Data, offset: Int, qos: UInt8) throws -> (String, UInt16?, Data, Int) {
179
+ var off = offset
180
+ let (topic, next) = try decodeString(data, offset: off)
181
+ off = next
182
+ var pid: UInt16? = nil
183
+ if qos > 0 {
184
+ if off + 2 > data.count { throw MQTTProtocolError.insufficientData("PUBLISH packet ID") }
185
+ pid = (UInt16(data[off]) << 8) | UInt16(data[off + 1])
186
+ off += 2
187
+ }
188
+ let payload = data.subdata(in: off..<data.count)
189
+ return (topic, pid, payload, data.count)
190
+ }
191
+
192
+ // MARK: - PUBACK
193
+
194
+ public static func buildPuback(packetId: UInt16) -> Data {
195
+ var out = Data()
196
+ out.append(MQTTMessageType.PUBACK.rawValue)
197
+ out.append(contentsOf: try! encodeRemainingLength(2))
198
+ out.append(UInt8((packetId >> 8) & 0xFF))
199
+ out.append(UInt8(packetId & 0xFF))
200
+ return out
201
+ }
202
+
203
+ public static func parsePuback(_ data: Data, offset: Int = 0) throws -> UInt16 {
204
+ if offset + 2 > data.count { throw MQTTProtocolError.insufficientData("PUBACK") }
205
+ return (UInt16(data[offset]) << 8) | UInt16(data[offset + 1])
206
+ }
207
+
208
+ // MARK: - SUBSCRIBE
209
+
210
+ public static func buildSubscribe(packetId: UInt16, topic: String, qos: UInt8 = 0) throws -> Data {
211
+ var out = Data()
212
+ out.append(MQTTMessageType.SUBSCRIBE.rawValue | 0x02) // QoS 1 required
213
+ var vh = Data()
214
+ vh.append(UInt8((packetId >> 8) & 0xFF))
215
+ vh.append(UInt8(packetId & 0xFF))
216
+ var pl = Data()
217
+ pl.append(try encodeString(topic))
218
+ pl.append(qos & 0x03)
219
+ let rem = vh.count + pl.count
220
+ out.append(contentsOf: try encodeRemainingLength(rem))
221
+ out.append(vh)
222
+ out.append(pl)
223
+ return out
224
+ }
225
+
226
+ /// Parse SUBSCRIBE. Returns (packetId, topic, qos, newOffset).
227
+ public static func parseSubscribe(_ data: Data, offset: Int = 0) throws -> (UInt16, String, UInt8, Int) {
228
+ var off = offset
229
+ if off + 2 > data.count { throw MQTTProtocolError.insufficientData("SUBSCRIBE packet ID") }
230
+ let pid = (UInt16(data[off]) << 8) | UInt16(data[off + 1])
231
+ off += 2
232
+ let (topic, next) = try decodeString(data, offset: off)
233
+ off = next
234
+ if off >= data.count { throw MQTTProtocolError.insufficientData("SUBSCRIBE QoS") }
235
+ let qos = data[off] & 0x03
236
+ off += 1
237
+ return (pid, topic, qos, off)
238
+ }
239
+
240
+ // MARK: - SUBACK
241
+
242
+ public static func buildSuback(packetId: UInt16, returnCode: UInt8 = 0) -> Data {
243
+ var out = Data()
244
+ out.append(MQTTMessageType.SUBACK.rawValue)
245
+ out.append(contentsOf: try! encodeRemainingLength(3))
246
+ out.append(UInt8((packetId >> 8) & 0xFF))
247
+ out.append(UInt8(packetId & 0xFF))
248
+ out.append(returnCode)
249
+ return out
250
+ }
251
+
252
+ /// Parse SUBACK. Returns (packetId, returnCode, newOffset).
253
+ public static func parseSuback(_ data: Data, offset: Int = 0) throws -> (UInt16, UInt8, Int) {
254
+ if offset + 3 > data.count { throw MQTTProtocolError.insufficientData("SUBACK") }
255
+ let pid = (UInt16(data[offset]) << 8) | UInt16(data[offset + 1])
256
+ let rc = data[offset + 2]
257
+ return (pid, rc, offset + 3)
258
+ }
259
+
260
+ // MARK: - UNSUBSCRIBE / UNSUBACK
261
+
262
+ public static func buildUnsubscribe(packetId: UInt16, topics: [String]) throws -> Data {
263
+ var vh = Data()
264
+ vh.append(UInt8((packetId >> 8) & 0xFF))
265
+ vh.append(UInt8(packetId & 0xFF))
266
+ var pl = Data()
267
+ for t in topics { pl.append(try encodeString(t)) }
268
+ let rem = vh.count + pl.count
269
+ var out = Data()
270
+ out.append(MQTTMessageType.UNSUBSCRIBE.rawValue | 0x02)
271
+ out.append(contentsOf: try encodeRemainingLength(rem))
272
+ out.append(vh)
273
+ out.append(pl)
274
+ return out
275
+ }
276
+
277
+ public static func buildUnsuback(packetId: UInt16) -> Data {
278
+ var out = Data()
279
+ out.append(MQTTMessageType.UNSUBACK.rawValue)
280
+ out.append(contentsOf: try! encodeRemainingLength(2))
281
+ out.append(UInt8((packetId >> 8) & 0xFF))
282
+ out.append(UInt8(packetId & 0xFF))
283
+ return out
284
+ }
285
+
286
+ public static func parseUnsubscribe(_ data: Data, offset: Int = 0) throws -> (UInt16, [String], Int) {
287
+ var off = offset
288
+ if off + 2 > data.count { throw MQTTProtocolError.insufficientData("UNSUBSCRIBE packet ID") }
289
+ let pid = (UInt16(data[off]) << 8) | UInt16(data[off + 1])
290
+ off += 2
291
+ var topics: [String] = []
292
+ while off < data.count {
293
+ let (t, next) = try decodeString(data, offset: off)
294
+ topics.append(t)
295
+ off = next
296
+ }
297
+ return (pid, topics, off)
298
+ }
299
+
300
+ // MARK: - PINGREQ / PINGRESP / DISCONNECT
301
+
302
+ public static func buildPingreq() -> Data {
303
+ var out = Data()
304
+ out.append(MQTTMessageType.PINGREQ.rawValue)
305
+ out.append(contentsOf: try! encodeRemainingLength(0))
306
+ return out
307
+ }
308
+
309
+ public static func buildPingresp() -> Data {
310
+ var out = Data()
311
+ out.append(MQTTMessageType.PINGRESP.rawValue)
312
+ out.append(contentsOf: try! encodeRemainingLength(0))
313
+ return out
314
+ }
315
+
316
+ public static func buildDisconnect() -> Data {
317
+ var out = Data()
318
+ out.append(MQTTMessageType.DISCONNECT.rawValue)
319
+ out.append(contentsOf: try! encodeRemainingLength(0))
320
+ return out
321
+ }
322
+ }
@@ -0,0 +1,54 @@
1
+ //
2
+ // MQTTTypes.swift
3
+ // MqttQuicPlugin
4
+ //
5
+ // MQTT packet types and constants. Matches MQTTD protocol.py / protocol_v5.py.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ /// MQTT 3.1.1 / 5.0 message types (fixed header first nibble)
11
+ public enum MQTTMessageType: UInt8 {
12
+ case CONNECT = 0x10
13
+ case CONNACK = 0x20
14
+ case PUBLISH = 0x30
15
+ case PUBACK = 0x40
16
+ case PUBREC = 0x50
17
+ case PUBREL = 0x62
18
+ case PUBCOMP = 0x70
19
+ case SUBSCRIBE = 0x82
20
+ case SUBACK = 0x90
21
+ case UNSUBSCRIBE = 0xA2
22
+ case UNSUBACK = 0xB0
23
+ case PINGREQ = 0xC0
24
+ case PINGRESP = 0xD0
25
+ case DISCONNECT = 0xE0
26
+ }
27
+
28
+ /// CONNECT flags
29
+ public struct MQTTConnectFlags {
30
+ public static let username: UInt8 = 0x80
31
+ public static let password: UInt8 = 0x40
32
+ public static let willRetain: UInt8 = 0x20
33
+ public static let willQoS1: UInt8 = 0x08
34
+ public static let willQoS2: UInt8 = 0x18
35
+ public static let willFlag: UInt8 = 0x04
36
+ public static let cleanSession: UInt8 = 0x02
37
+ public static let reserved: UInt8 = 0x01
38
+ }
39
+
40
+ /// CONNACK return codes (MQTT 3.1.1)
41
+ public enum MQTTConnAckCode: UInt8 {
42
+ case accepted = 0x00
43
+ case unacceptableProtocol = 0x01
44
+ case identifierRejected = 0x02
45
+ case serverUnavailable = 0x03
46
+ case badUsernamePassword = 0x04
47
+ case notAuthorized = 0x05
48
+ }
49
+
50
+ /// Protocol levels
51
+ public struct MQTTProtocolLevel {
52
+ public static let v311: UInt8 = 0x04
53
+ public static let v5: UInt8 = 0x05
54
+ }