@annadata/capacitor-mqtt-quic 0.1.8 → 0.1.9

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 (67) hide show
  1. package/android/build-wolfssl.sh +8 -2
  2. package/android/install/nghttp3-android/arm64-v8a/lib/libnghttp3.a +0 -0
  3. package/android/install/nghttp3-android/arm64-v8a/lib/libnghttp3.so +0 -0
  4. package/android/install/nghttp3-android/arm64-v8a/lib/pkgconfig/libnghttp3.pc +4 -4
  5. package/android/install/nghttp3-android/armeabi-v7a/lib/libnghttp3.a +0 -0
  6. package/android/install/nghttp3-android/armeabi-v7a/lib/libnghttp3.so +0 -0
  7. package/android/install/nghttp3-android/armeabi-v7a/lib/pkgconfig/libnghttp3.pc +4 -4
  8. package/android/install/nghttp3-android/x86_64/lib/libnghttp3.a +0 -0
  9. package/android/install/nghttp3-android/x86_64/lib/libnghttp3.so +0 -0
  10. package/android/install/nghttp3-android/x86_64/lib/pkgconfig/libnghttp3.pc +4 -4
  11. package/android/install/ngtcp2-android/arm64-v8a/lib/libngtcp2.a +0 -0
  12. package/android/install/ngtcp2-android/arm64-v8a/lib/libngtcp2.so +0 -0
  13. package/android/install/ngtcp2-android/arm64-v8a/lib/libngtcp2_crypto_wolfssl.a +0 -0
  14. package/android/install/ngtcp2-android/arm64-v8a/lib/libngtcp2_crypto_wolfssl.so +0 -0
  15. package/android/install/ngtcp2-android/arm64-v8a/lib/pkgconfig/libngtcp2.pc +4 -4
  16. package/android/install/ngtcp2-android/arm64-v8a/lib/pkgconfig/libngtcp2_crypto_wolfssl.pc +4 -4
  17. package/android/install/ngtcp2-android/armeabi-v7a/lib/libngtcp2.a +0 -0
  18. package/android/install/ngtcp2-android/armeabi-v7a/lib/libngtcp2.so +0 -0
  19. package/android/install/ngtcp2-android/armeabi-v7a/lib/libngtcp2_crypto_wolfssl.a +0 -0
  20. package/android/install/ngtcp2-android/armeabi-v7a/lib/libngtcp2_crypto_wolfssl.so +0 -0
  21. package/android/install/ngtcp2-android/armeabi-v7a/lib/pkgconfig/libngtcp2.pc +4 -4
  22. package/android/install/ngtcp2-android/armeabi-v7a/lib/pkgconfig/libngtcp2_crypto_wolfssl.pc +4 -4
  23. package/android/install/ngtcp2-android/x86_64/lib/libngtcp2.a +0 -0
  24. package/android/install/ngtcp2-android/x86_64/lib/libngtcp2.so +0 -0
  25. package/android/install/ngtcp2-android/x86_64/lib/libngtcp2_crypto_wolfssl.a +0 -0
  26. package/android/install/ngtcp2-android/x86_64/lib/libngtcp2_crypto_wolfssl.so +0 -0
  27. package/android/install/ngtcp2-android/x86_64/lib/pkgconfig/libngtcp2.pc +4 -4
  28. package/android/install/ngtcp2-android/x86_64/lib/pkgconfig/libngtcp2_crypto_wolfssl.pc +4 -4
  29. package/android/install/wolfssl-android/arm64-v8a/bin/wolfssl-config +1 -1
  30. package/android/install/wolfssl-android/arm64-v8a/lib/libwolfssl.a +0 -0
  31. package/android/install/wolfssl-android/arm64-v8a/lib/libwolfssl.la +1 -1
  32. package/android/install/wolfssl-android/arm64-v8a/lib/pkgconfig/wolfssl.pc +1 -1
  33. package/android/install/wolfssl-android/armeabi-v7a/bin/wolfssl-config +1 -1
  34. package/android/install/wolfssl-android/armeabi-v7a/lib/libwolfssl.a +0 -0
  35. package/android/install/wolfssl-android/armeabi-v7a/lib/libwolfssl.la +1 -1
  36. package/android/install/wolfssl-android/armeabi-v7a/lib/pkgconfig/wolfssl.pc +1 -1
  37. package/android/install/wolfssl-android/x86_64/bin/wolfssl-config +1 -1
  38. package/android/install/wolfssl-android/x86_64/lib/libwolfssl.a +0 -0
  39. package/android/install/wolfssl-android/x86_64/lib/libwolfssl.la +1 -1
  40. package/android/install/wolfssl-android/x86_64/lib/pkgconfig/wolfssl.pc +1 -1
  41. package/android/src/main/cpp/ngtcp2_jni.cpp +94 -19
  42. package/android/src/main/kotlin/ai/annadata/mqttquic/MqttQuicPlugin.kt +58 -3
  43. package/android/src/main/kotlin/ai/annadata/mqttquic/client/MQTTClient.kt +230 -80
  44. package/android/src/main/kotlin/ai/annadata/mqttquic/mqtt/MQTTProtocol.kt +33 -2
  45. package/android/src/main/kotlin/ai/annadata/mqttquic/quic/NGTCP2Client.kt +25 -15
  46. package/android/src/main/kotlin/ai/annadata/mqttquic/quic/QuicClientStub.kt +1 -1
  47. package/android/src/main/kotlin/ai/annadata/mqttquic/quic/QuicTypes.kt +1 -1
  48. package/android/src/main/kotlin/ai/annadata/mqttquic/transport/QUICStreamAdapter.kt +80 -5
  49. package/android/src/main/kotlin/ai/annadata/mqttquic/transport/StreamTransport.kt +4 -0
  50. package/docs/diff-node_modules-vs-standalone-android-src.patch +1031 -0
  51. package/ios/build-wolfssl.sh +8 -3
  52. package/ios/libs/libnghttp3.a +0 -0
  53. package/ios/libs/libngtcp2.a +0 -0
  54. package/ios/libs/libngtcp2_crypto_wolfssl.a +0 -0
  55. package/ios/libs/libwolfssl.a +0 -0
  56. package/ios/libs-simulator/libnghttp3.a +0 -0
  57. package/ios/libs-simulator/libngtcp2.a +0 -0
  58. package/ios/libs-simulator/libngtcp2_crypto_wolfssl.a +0 -0
  59. package/ios/libs-simulator/libwolfssl.a +0 -0
  60. package/ios/libs-simulator-x86_64/libnghttp3.a +0 -0
  61. package/ios/libs-simulator-x86_64/libngtcp2.a +0 -0
  62. package/ios/libs-simulator-x86_64/libngtcp2_crypto_wolfssl.a +0 -0
  63. package/ios/libs-simulator-x86_64/libwolfssl.a +0 -0
  64. package/package.json +1 -1
  65. package/ios/libs/MqttQuicLibs.xcframework/Info.plist +0 -44
  66. package/ios/libs/MqttQuicLibs.xcframework/ios-arm64/libmqttquic_native_device.a +0 -0
  67. package/ios/libs/MqttQuicLibs.xcframework/ios-arm64_x86_64-simulator/libmqttquic_native_simulator.a +0 -0
@@ -1,21 +1,96 @@
1
1
  package ai.annadata.mqttquic.transport
2
2
 
3
+ import android.util.Log
4
+ import ai.annadata.mqttquic.mqtt.MQTTProtocol
3
5
  import ai.annadata.mqttquic.quic.QuicStream
6
+ import kotlinx.coroutines.delay
4
7
 
5
8
  /**
6
- * MQTTStreamReader over QUIC stream. Mirrors NGTCP2StreamReader.
9
+ * MQTTStreamReader over QUIC stream. Buffers excess bytes so readexactly(n)
10
+ * and read(maxBytes) get exactly the requested amount; native may return
11
+ * a full CONNACK (e.g. 18 bytes) in one read, so we must not lose the remainder.
12
+ *
13
+ * Efficient CONNACK/packet read: call [drain] to read until the stream has no more
14
+ * data, then [tryConsumeNextPacket] to take the first complete MQTT packet from
15
+ * the buffer. Repeat drain + tryConsumeNextPacket (with short delay) until you
16
+ * get a packet or timeout.
7
17
  */
8
18
  class QUICStreamReader(private val stream: QuicStream) : MQTTStreamReader {
9
19
 
10
- override suspend fun read(maxBytes: Int): ByteArray = stream.read(maxBytes)
20
+ private val buffer = mutableListOf<Byte>()
21
+
22
+ override suspend fun available(): Int = buffer.size
23
+
24
+ /** Read from stream until no more data is available (drained). Call before tryConsumeNextPacket. */
25
+ suspend fun drain() {
26
+ while (true) {
27
+ val chunk = stream.read(8192)
28
+ if (chunk.isEmpty()) break
29
+ Log.i("MQTTClient", "QUICStreamReader: drain got ${chunk.size} bytes bufferTotal=${buffer.size + chunk.size}")
30
+ buffer.addAll(chunk.toList())
31
+ }
32
+ }
33
+
34
+ /** Consume the first n bytes from buffer and return them. Caller must ensure buffer.size >= n. */
35
+ fun consume(n: Int): ByteArray {
36
+ if (buffer.size < n) throw IllegalArgumentException("buffer has ${buffer.size} < $n")
37
+ val out = buffer.take(n).toByteArray()
38
+ repeat(n) { buffer.removeAt(0) }
39
+ return out
40
+ }
41
+
42
+ /**
43
+ * If buffer contains at least one complete MQTT packet (fixed header + payload), consume and return it; else return null.
44
+ * Call after [drain]; if null, delay and drain again (or timeout).
45
+ */
46
+ fun tryConsumeNextPacket(): ByteArray? {
47
+ val buf = buffer.toByteArray()
48
+ val totalLen = MQTTProtocol.getNextPacketLength(buf)
49
+ if (totalLen == null) {
50
+ if (buf.isNotEmpty()) {
51
+ Log.w("MQTTClient", "QUICStreamReader: getNextPacketLength returned null bufferSize=${buf.size} firstByte=0x${Integer.toHexString(buf[0].toInt() and 0xFF)}")
52
+ }
53
+ return null
54
+ }
55
+ if (buffer.size < totalLen) {
56
+ Log.i("MQTTClient", "QUICStreamReader: buffer.size=${buffer.size} < totalLen=$totalLen waiting for more")
57
+ return null
58
+ }
59
+ val packet = consume(totalLen)
60
+ Log.i("MQTTClient", "QUICStreamReader: tryConsumeNextPacket consumed $totalLen bytes type=0x${Integer.toHexString(packet[0].toInt() and 0xFF)}")
61
+ return packet
62
+ }
63
+
64
+ override suspend fun read(maxBytes: Int): ByteArray {
65
+ while (buffer.size < maxBytes) {
66
+ val chunk = stream.read(maxBytes - buffer.size)
67
+ if (chunk.isEmpty()) break
68
+ Log.i("MQTTClient", "QUICStreamReader: got chunk=${chunk.size} bufferSize=${buffer.size + chunk.size}")
69
+ buffer.addAll(chunk.toList())
70
+ }
71
+ val n = minOf(maxBytes, buffer.size)
72
+ if (n == 0) return ByteArray(0)
73
+ val result = buffer.subList(0, n).toByteArray()
74
+ repeat(n) { buffer.removeAt(0) }
75
+ Log.i("MQTTClient", "QUICStreamReader: returning $n bytes bufferRemain=${buffer.size}")
76
+ return result
77
+ }
11
78
 
12
79
  override suspend fun readexactly(n: Int): ByteArray {
80
+ Log.i("MQTTClient", "QUICStreamReader: readexactly($n) bufferHas=${buffer.size}")
13
81
  val acc = mutableListOf<Byte>()
14
82
  while (acc.size < n) {
15
- val chunk = stream.read(n - acc.size)
16
- if (chunk.isEmpty()) throw IllegalArgumentException("readexactly")
17
- acc.addAll(chunk.toList())
83
+ drain()
84
+ val fromBuffer = minOf(n - acc.size, buffer.size)
85
+ if (fromBuffer > 0) {
86
+ acc.addAll(buffer.subList(0, fromBuffer).toList())
87
+ repeat(fromBuffer) { buffer.removeAt(0) }
88
+ } else {
89
+ // No data yet (e.g. message loop waiting for SUBACK/PUBLISH). Wait and retry instead of throwing.
90
+ delay(20L)
91
+ }
18
92
  }
93
+ Log.i("MQTTClient", "QUICStreamReader: readexactly($n) done")
19
94
  return acc.toByteArray()
20
95
  }
21
96
  }
@@ -7,6 +7,8 @@ import kotlinx.coroutines.delay
7
7
  * Phase 2 implements over QUIC stream.
8
8
  */
9
9
  interface MQTTStreamReader {
10
+ /** Number of bytes currently buffered (without reading from stream). Used to skip unwanted packets only when safe. */
11
+ suspend fun available(): Int
10
12
  suspend fun read(maxBytes: Int): ByteArray
11
13
  suspend fun readexactly(n: Int): ByteArray
12
14
  }
@@ -50,6 +52,8 @@ class MockStreamBuffer(initialReadData: ByteArray = ByteArray(0)) {
50
52
  */
51
53
  class MockStreamReader(private val buffer: MockStreamBuffer) : MQTTStreamReader {
52
54
 
55
+ override suspend fun available(): Int = buffer.readBuffer.size
56
+
53
57
  override suspend fun read(maxBytes: Int): ByteArray {
54
58
  if (buffer.isClosed && buffer.readBuffer.isEmpty()) return ByteArray(0)
55
59
  val n = minOf(maxBytes, buffer.readBuffer.size)