@annadata/capacitor-mqtt-quic 0.2.0 → 0.2.1
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.
- package/android/install/wolfssl-android/arm64-v8a/lib/libwolfssl.a +0 -0
- package/android/install/wolfssl-android/armeabi-v7a/lib/libwolfssl.a +0 -0
- package/android/install/wolfssl-android/x86_64/lib/libwolfssl.a +0 -0
- package/ios/Sources/MqttQuicPlugin/Client/MQTTClient.swift +62 -0
- package/ios/Sources/MqttQuicPlugin/MqttQuicPlugin.swift +19 -7
- package/ios/libs/libnghttp3.a +0 -0
- package/ios/libs/libngtcp2.a +0 -0
- package/ios/libs/libngtcp2_crypto_wolfssl.a +0 -0
- package/ios/libs/libwolfssl.a +0 -0
- package/ios/libs-simulator/libnghttp3.a +0 -0
- package/ios/libs-simulator/libngtcp2.a +0 -0
- package/ios/libs-simulator/libngtcp2_crypto_wolfssl.a +0 -0
- package/ios/libs-simulator/libwolfssl.a +0 -0
- package/ios/libs-simulator-x86_64/libnghttp3.a +0 -0
- package/ios/libs-simulator-x86_64/libngtcp2.a +0 -0
- package/ios/libs-simulator-x86_64/libngtcp2_crypto_wolfssl.a +0 -0
- package/ios/libs-simulator-x86_64/libwolfssl.a +0 -0
- package/package.json +1 -1
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -46,6 +46,8 @@ public final class MQTTClient {
|
|
|
46
46
|
private var subackContinuation: CheckedContinuation<Data, Error>?
|
|
47
47
|
/// Handoff from message loop to unsubscribe() for UNSUBACK.
|
|
48
48
|
private var unsubackContinuation: CheckedContinuation<Data, Error>?
|
|
49
|
+
/// Pending PINGRESP for sendMqttPing(). Message loop completes when PINGRESP is read.
|
|
50
|
+
private var pendingPingresp: CheckedContinuation<Void, Error>?
|
|
49
51
|
private let lock = NSLock()
|
|
50
52
|
|
|
51
53
|
public init(protocolVersion: ProtocolVersion = .auto) {
|
|
@@ -345,6 +347,8 @@ public final class MQTTClient {
|
|
|
345
347
|
subackContinuation = nil
|
|
346
348
|
let unsubCont = unsubackContinuation
|
|
347
349
|
unsubackContinuation = nil
|
|
350
|
+
let pingCont = pendingPingresp
|
|
351
|
+
pendingPingresp = nil
|
|
348
352
|
let w = writer
|
|
349
353
|
let version = activeProtocolVersion
|
|
350
354
|
quicClient = nil
|
|
@@ -358,6 +362,7 @@ public final class MQTTClient {
|
|
|
358
362
|
lock.unlock()
|
|
359
363
|
subCont?.resume(throwing: MQTTProtocolError.insufficientData("disconnected"))
|
|
360
364
|
unsubCont?.resume(throwing: MQTTProtocolError.insufficientData("disconnected"))
|
|
365
|
+
pingCont?.resume(throwing: MQTTProtocolError.insufficientData("disconnected"))
|
|
361
366
|
|
|
362
367
|
if let w = w {
|
|
363
368
|
let data: Data
|
|
@@ -403,6 +408,55 @@ public final class MQTTClient {
|
|
|
403
408
|
return (fixed[0], rem, fixed)
|
|
404
409
|
}
|
|
405
410
|
|
|
411
|
+
/// Send MQTT PINGREQ and wait for PINGRESP. Resets server's idle timer. Returns true if PINGRESP received within timeout.
|
|
412
|
+
public func sendMqttPing(timeoutMs: UInt64 = 5000) async -> Bool {
|
|
413
|
+
guard case .connected = getState() else { return false }
|
|
414
|
+
lock.lock()
|
|
415
|
+
if pendingPingresp != nil {
|
|
416
|
+
lock.unlock()
|
|
417
|
+
return false
|
|
418
|
+
}
|
|
419
|
+
let w = writer
|
|
420
|
+
lock.unlock()
|
|
421
|
+
do {
|
|
422
|
+
try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in
|
|
423
|
+
lock.lock()
|
|
424
|
+
pendingPingresp = cont
|
|
425
|
+
lock.unlock()
|
|
426
|
+
Task {
|
|
427
|
+
try? await Task.sleep(nanoseconds: timeoutMs * 1_000_000)
|
|
428
|
+
self.lock.lock()
|
|
429
|
+
if let c = self.pendingPingresp {
|
|
430
|
+
self.pendingPingresp = nil
|
|
431
|
+
c.resume(throwing: MQTTProtocolError.insufficientData("PINGRESP timeout"))
|
|
432
|
+
}
|
|
433
|
+
self.lock.unlock()
|
|
434
|
+
}
|
|
435
|
+
if let w = w {
|
|
436
|
+
Task {
|
|
437
|
+
do {
|
|
438
|
+
try await w.write(MQTTProtocol.buildPingreq())
|
|
439
|
+
try await w.drain()
|
|
440
|
+
} catch {
|
|
441
|
+
self.lock.lock()
|
|
442
|
+
if let c = self.pendingPingresp {
|
|
443
|
+
self.pendingPingresp = nil
|
|
444
|
+
c.resume(throwing: error)
|
|
445
|
+
}
|
|
446
|
+
self.lock.unlock()
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return true
|
|
452
|
+
} catch {
|
|
453
|
+
lock.lock()
|
|
454
|
+
pendingPingresp = nil
|
|
455
|
+
lock.unlock()
|
|
456
|
+
return false
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
406
460
|
/// Send PINGREQ at effectiveKeepalive interval so server sees activity and does not close (idle/keepalive). [MQTT-3.1.2-20]
|
|
407
461
|
private func startKeepaliveLoop() {
|
|
408
462
|
keepaliveTask?.cancel()
|
|
@@ -493,6 +547,12 @@ public final class MQTTClient {
|
|
|
493
547
|
try await w.write(Data(pr))
|
|
494
548
|
try await w.drain()
|
|
495
549
|
}
|
|
550
|
+
case MQTTMessageType.PINGRESP.rawValue:
|
|
551
|
+
self.lock.lock()
|
|
552
|
+
let pingCont = self.pendingPingresp
|
|
553
|
+
self.pendingPingresp = nil
|
|
554
|
+
self.lock.unlock()
|
|
555
|
+
pingCont?.resume()
|
|
496
556
|
case MQTTMessageType.PUBLISH.rawValue:
|
|
497
557
|
let qos = (msgType >> 1) & 0x03
|
|
498
558
|
let topic: String
|
|
@@ -550,6 +610,8 @@ public final class MQTTClient {
|
|
|
550
610
|
subackContinuation = nil
|
|
551
611
|
unsubackContinuation?.resume(throwing: error)
|
|
552
612
|
unsubackContinuation = nil
|
|
613
|
+
pendingPingresp?.resume(throwing: error)
|
|
614
|
+
pendingPingresp = nil
|
|
553
615
|
state = .disconnected
|
|
554
616
|
lock.unlock()
|
|
555
617
|
}
|
|
@@ -15,6 +15,7 @@ public class MqttQuicPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
15
15
|
public let jsName = "MqttQuic"
|
|
16
16
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
17
17
|
CAPPluginMethod(name: "ping", returnType: CAPPluginReturnPromise),
|
|
18
|
+
CAPPluginMethod(name: "sendKeepalive", returnType: CAPPluginReturnPromise),
|
|
18
19
|
CAPPluginMethod(name: "connect", returnType: CAPPluginReturnPromise),
|
|
19
20
|
CAPPluginMethod(name: "disconnect", returnType: CAPPluginReturnPromise),
|
|
20
21
|
CAPPluginMethod(name: "publish", returnType: CAPPluginReturnPromise),
|
|
@@ -27,6 +28,19 @@ public class MqttQuicPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
27
28
|
|
|
28
29
|
@objc override public func load() {}
|
|
29
30
|
|
|
31
|
+
@objc func sendKeepalive(_ call: CAPPluginCall) {
|
|
32
|
+
let timeoutMs = UInt64(call.getInt("timeoutMs") ?? 5000)
|
|
33
|
+
let clamped = min(max(timeoutMs, 1000), 15000)
|
|
34
|
+
Task {
|
|
35
|
+
do {
|
|
36
|
+
let ok = await client.sendMqttPing(timeoutMs: clamped)
|
|
37
|
+
DispatchQueue.main.async { call.resolve(["ok": ok]) }
|
|
38
|
+
} catch {
|
|
39
|
+
DispatchQueue.main.async { call.reject("\(error)") }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
30
44
|
@objc func ping(_ call: CAPPluginCall) {
|
|
31
45
|
let host = call.getString("host") ?? ""
|
|
32
46
|
let port = call.getInt("port") ?? 1884
|
|
@@ -120,14 +134,12 @@ public class MqttQuicPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
120
134
|
// Forward every incoming PUBLISH to JS so addListener('message', ...) receives topic + payload (matches Android)
|
|
121
135
|
client.onPublish = { [weak self] topic, payload in
|
|
122
136
|
guard let self = self else { return }
|
|
123
|
-
let payloadStr: String
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
payloadStr = payload.base64EncodedString()
|
|
128
|
-
}
|
|
137
|
+
let payloadStr: String = {
|
|
138
|
+
if let str = String(data: payload, encoding: .utf8) { return str }
|
|
139
|
+
return payload.base64EncodedString()
|
|
140
|
+
}()
|
|
129
141
|
DispatchQueue.main.async {
|
|
130
|
-
self.notifyListeners("message", data: ["topic": topic, "payload": payloadStr])
|
|
142
|
+
self.notifyListeners("message", data: ["topic": topic as String, "payload": payloadStr as String])
|
|
131
143
|
}
|
|
132
144
|
}
|
|
133
145
|
try await client.connect(
|
package/ios/libs/libnghttp3.a
CHANGED
|
Binary file
|
package/ios/libs/libngtcp2.a
CHANGED
|
Binary file
|
|
Binary file
|
package/ios/libs/libwolfssl.a
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@annadata/capacitor-mqtt-quic",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MQTT-over-QUIC client for Capacitor (iOS, Android, Web). Native: ngtcp2+WolfSSL; Web: MQTT over WebSocket (WSS), same API.",
|
|
6
6
|
"main": "dist/plugin.cjs.js",
|