@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.
- package/README.md +399 -0
- package/android/NGTCP2_BUILD_INSTRUCTIONS.md +319 -0
- package/android/build-nghttp3.sh +182 -0
- package/android/build-ngtcp2.sh +289 -0
- package/android/build-openssl.sh +302 -0
- package/android/build.gradle +75 -0
- package/android/gradle.properties +3 -0
- package/android/proguard-rules.pro +2 -0
- package/android/settings.gradle +1 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/assets/mqttquic_ca.pem +5 -0
- package/android/src/main/cpp/CMakeLists.txt +157 -0
- package/android/src/main/cpp/ngtcp2_jni.cpp +928 -0
- package/android/src/main/kotlin/ai/annadata/mqttquic/MqttQuicPlugin.kt +232 -0
- package/android/src/main/kotlin/ai/annadata/mqttquic/client/MQTTClient.kt +339 -0
- package/android/src/main/kotlin/ai/annadata/mqttquic/mqtt/MQTT5Properties.kt +250 -0
- package/android/src/main/kotlin/ai/annadata/mqttquic/mqtt/MQTT5Protocol.kt +281 -0
- package/android/src/main/kotlin/ai/annadata/mqttquic/mqtt/MQTT5ReasonCodes.kt +109 -0
- package/android/src/main/kotlin/ai/annadata/mqttquic/mqtt/MQTTProtocol.kt +249 -0
- package/android/src/main/kotlin/ai/annadata/mqttquic/mqtt/MQTTTypes.kt +47 -0
- package/android/src/main/kotlin/ai/annadata/mqttquic/quic/NGTCP2Client.kt +184 -0
- package/android/src/main/kotlin/ai/annadata/mqttquic/quic/QuicClientStub.kt +54 -0
- package/android/src/main/kotlin/ai/annadata/mqttquic/quic/QuicTypes.kt +21 -0
- package/android/src/main/kotlin/ai/annadata/mqttquic/transport/QUICStreamAdapter.kt +37 -0
- package/android/src/main/kotlin/ai/annadata/mqttquic/transport/StreamTransport.kt +91 -0
- package/android/src/test/kotlin/ai/annadata/mqttquic/mqtt/MQTTProtocolTest.kt +92 -0
- package/dist/esm/definitions.d.ts +66 -0
- package/dist/esm/definitions.d.ts.map +1 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +28 -0
- package/dist/esm/web.d.ts.map +1 -0
- package/dist/esm/web.js +183 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +217 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +215 -0
- package/dist/plugin.js.map +1 -0
- package/ios/MqttQuicPlugin.podspec +34 -0
- package/ios/NGTCP2_BUILD_INSTRUCTIONS.md +302 -0
- package/ios/Sources/MqttQuicPlugin/Client/MQTTClient.swift +343 -0
- package/ios/Sources/MqttQuicPlugin/MQTT/MQTT5Properties.swift +280 -0
- package/ios/Sources/MqttQuicPlugin/MQTT/MQTT5Protocol.swift +333 -0
- package/ios/Sources/MqttQuicPlugin/MQTT/MQTT5ReasonCodes.swift +113 -0
- package/ios/Sources/MqttQuicPlugin/MQTT/MQTTProtocol.swift +322 -0
- package/ios/Sources/MqttQuicPlugin/MQTT/MQTTTypes.swift +54 -0
- package/ios/Sources/MqttQuicPlugin/MqttQuicPlugin.swift +229 -0
- package/ios/Sources/MqttQuicPlugin/QUIC/NGTCP2Bridge.h +29 -0
- package/ios/Sources/MqttQuicPlugin/QUIC/NGTCP2Bridge.mm +865 -0
- package/ios/Sources/MqttQuicPlugin/QUIC/NGTCP2Client.swift +262 -0
- package/ios/Sources/MqttQuicPlugin/QUIC/QuicClientStub.swift +66 -0
- package/ios/Sources/MqttQuicPlugin/QUIC/QuicTypes.swift +23 -0
- package/ios/Sources/MqttQuicPlugin/Resources/mqttquic_ca.pem +5 -0
- package/ios/Sources/MqttQuicPlugin/Transport/QUICStreamAdapter.swift +50 -0
- package/ios/Sources/MqttQuicPlugin/Transport/StreamTransport.swift +105 -0
- package/ios/Tests/MQTTProtocolTests.swift +82 -0
- package/ios/build-nghttp3.sh +173 -0
- package/ios/build-ngtcp2.sh +278 -0
- package/ios/build-openssl.sh +405 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
# @annadata/capacitor-mqtt-quic
|
|
2
|
+
|
|
3
|
+
MQTT-over-QUIC Capacitor plugin for iOS and Android. Uses ngtcp2 for QUIC on native; MQTT over WebSocket (WSS) fallback on web.
|
|
4
|
+
|
|
5
|
+
**Features:**
|
|
6
|
+
- ✅ **MQTT 5.0** support with full properties and reason codes
|
|
7
|
+
- ✅ **MQTT 3.1.1** support (backward compatible)
|
|
8
|
+
- ✅ Automatic protocol negotiation (tries 5.0, falls back to 3.1.1)
|
|
9
|
+
- ✅ QUIC transport (ngtcp2) - currently using stubs (see ngtcp2 build section)
|
|
10
|
+
- ✅ Transport abstraction (StreamReader/StreamWriter)
|
|
11
|
+
- ✅ Full MQTT client API (connect, publish, subscribe, unsubscribe, disconnect)
|
|
12
|
+
|
|
13
|
+
## Structure
|
|
14
|
+
|
|
15
|
+
- **Phase 1**: MQTT protocol layer (Swift/Kotlin) - **Complete** ✅
|
|
16
|
+
- MQTT 3.1.1 protocol implementation
|
|
17
|
+
- MQTT 5.0 protocol implementation with properties and reason codes
|
|
18
|
+
- Transport abstraction (StreamReader/StreamWriter)
|
|
19
|
+
- **Phase 2**: QUIC transport (ngtcp2) + stream adapters - **In Progress** ⏳
|
|
20
|
+
- Currently uses stub implementations for testing
|
|
21
|
+
- See [NGTCP2_INTEGRATION_PLAN.md](./NGTCP2_INTEGRATION_PLAN.md) for build instructions
|
|
22
|
+
- **Phase 3**: MQTT client API + Capacitor plugin bridge - **Complete** ✅
|
|
23
|
+
- **Phase 4**: Platform integration in annadata-production - **Complete** ✅
|
|
24
|
+
|
|
25
|
+
## Plugin API
|
|
26
|
+
|
|
27
|
+
### Basic Usage (MQTT 3.1.1)
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { MqttQuic } from '@annadata/capacitor-mqtt-quic';
|
|
31
|
+
|
|
32
|
+
// Connect
|
|
33
|
+
await MqttQuic.connect({
|
|
34
|
+
host: 'mqtt.example.com',
|
|
35
|
+
port: 1884,
|
|
36
|
+
clientId: 'my-client-id',
|
|
37
|
+
username: 'user',
|
|
38
|
+
password: 'pass',
|
|
39
|
+
cleanSession: true,
|
|
40
|
+
keepalive: 60
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Publish
|
|
44
|
+
await MqttQuic.publish({
|
|
45
|
+
topic: 'sensors/temperature',
|
|
46
|
+
payload: '25.5',
|
|
47
|
+
qos: 1,
|
|
48
|
+
retain: false
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Subscribe
|
|
52
|
+
await MqttQuic.subscribe({
|
|
53
|
+
topic: 'sensors/+',
|
|
54
|
+
qos: 1
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Unsubscribe
|
|
58
|
+
await MqttQuic.unsubscribe({ topic: 'sensors/+' });
|
|
59
|
+
|
|
60
|
+
// Disconnect
|
|
61
|
+
await MqttQuic.disconnect();
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### TLS Certificate Verification (QUIC)
|
|
65
|
+
|
|
66
|
+
QUIC requires TLS 1.3 and certificate verification is **enabled by default**.
|
|
67
|
+
You can bundle a CA PEM and it will be loaded automatically:
|
|
68
|
+
|
|
69
|
+
- iOS: `ios/Sources/MqttQuicPlugin/Resources/mqttquic_ca.pem`
|
|
70
|
+
- Android: `android/src/main/assets/mqttquic_ca.pem`
|
|
71
|
+
|
|
72
|
+
You can also override per call:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
await MqttQuic.connect({
|
|
76
|
+
host: 'mqtt.example.com',
|
|
77
|
+
port: 1884,
|
|
78
|
+
clientId: 'my-client-id',
|
|
79
|
+
caFile: '/path/to/ca-bundle.pem',
|
|
80
|
+
// or caPath: '/path/to/ca-directory'
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### How to generate certificates
|
|
85
|
+
|
|
86
|
+
**Option A: Public CA (Let’s Encrypt)**
|
|
87
|
+
You do not bundle `mqttquic_ca.pem` (the OS already trusts public CAs).
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
sudo apt-get update
|
|
91
|
+
sudo apt-get install -y certbot
|
|
92
|
+
sudo certbot certonly --standalone -d mqtt.example.com
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Use on server:
|
|
96
|
+
- Cert: `/etc/letsencrypt/live/mqtt.example.com/fullchain.pem`
|
|
97
|
+
- Key: `/etc/letsencrypt/live/mqtt.example.com/privkey.pem`
|
|
98
|
+
|
|
99
|
+
**Option B: Private CA (dev/internal)**
|
|
100
|
+
Generate your own CA, sign the server cert, and bundle the CA PEM.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
mkdir -p certs && cd certs
|
|
104
|
+
|
|
105
|
+
# 1) Create CA (one-time)
|
|
106
|
+
openssl genrsa -out ca.key 4096
|
|
107
|
+
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.pem \
|
|
108
|
+
-subj "/C=US/ST=CA/L=SF/O=Annadata/OU=MQTT/CN=Annadata-Root-CA"
|
|
109
|
+
|
|
110
|
+
# 2) Create server key + CSR
|
|
111
|
+
openssl genrsa -out server.key 2048
|
|
112
|
+
openssl req -new -key server.key -out server.csr \
|
|
113
|
+
-subj "/C=US/ST=CA/L=SF/O=Annadata/OU=MQTT/CN=mqtt.example.com"
|
|
114
|
+
|
|
115
|
+
# 3) Add SANs (edit DNS/IP)
|
|
116
|
+
cat > server_ext.cnf <<EOF
|
|
117
|
+
subjectAltName = DNS:mqtt.example.com,IP:YOUR.SERVER.IP
|
|
118
|
+
extendedKeyUsage = serverAuth
|
|
119
|
+
EOF
|
|
120
|
+
|
|
121
|
+
# 4) Sign server cert
|
|
122
|
+
openssl x509 -req -in server.csr -CA ca.pem -CAkey ca.key -CAcreateserial \
|
|
123
|
+
-out server.pem -days 365 -sha256 -extfile server_ext.cnf
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Bundle the CA cert (never ship `ca.key`):
|
|
127
|
+
- iOS: `ios/Sources/MqttQuicPlugin/Resources/mqttquic_ca.pem` (use `ca.pem`)
|
|
128
|
+
- Android: `android/src/main/assets/mqttquic_ca.pem` (use `ca.pem`)
|
|
129
|
+
|
|
130
|
+
### Test Harness (QUIC Smoke Test)
|
|
131
|
+
|
|
132
|
+
This runs: connect → subscribe → publish → disconnect.
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
await MqttQuic.testHarness({
|
|
136
|
+
host: 'mqtt.example.com',
|
|
137
|
+
port: 1884,
|
|
138
|
+
clientId: 'mqttquic_test_client',
|
|
139
|
+
topic: 'test/topic',
|
|
140
|
+
payload: 'Hello QUIC!',
|
|
141
|
+
// optional CA override
|
|
142
|
+
caFile: '/path/to/ca-bundle.pem'
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### MQTT 5.0 Features
|
|
147
|
+
|
|
148
|
+
#### Protocol Version Selection
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
// Use MQTT 5.0 explicitly
|
|
152
|
+
await MqttQuic.connect({
|
|
153
|
+
host: 'mqtt.example.com',
|
|
154
|
+
port: 1884,
|
|
155
|
+
clientId: 'my-client-id',
|
|
156
|
+
protocolVersion: '5.0', // '3.1.1' | '5.0' | 'auto' (default)
|
|
157
|
+
sessionExpiryInterval: 3600, // Session persists 1 hour after disconnect
|
|
158
|
+
keepalive: 60
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Auto-negotiation (default): tries 5.0, falls back to 3.1.1
|
|
162
|
+
await MqttQuic.connect({
|
|
163
|
+
protocolVersion: 'auto', // or omit
|
|
164
|
+
// ...
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
#### Session Management
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
// Session expiry: control how long sessions persist after disconnect
|
|
172
|
+
await MqttQuic.connect({
|
|
173
|
+
// ...
|
|
174
|
+
protocolVersion: '5.0',
|
|
175
|
+
sessionExpiryInterval: 3600, // 1 hour in seconds
|
|
176
|
+
// 0 = session expires immediately on disconnect
|
|
177
|
+
// undefined/null = session expires on disconnect (default)
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
#### Message Expiry
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
// Messages auto-expire if not delivered within time limit
|
|
185
|
+
await MqttQuic.publish({
|
|
186
|
+
topic: 'events/urgent',
|
|
187
|
+
payload: 'Important message',
|
|
188
|
+
messageExpiryInterval: 300, // Expires in 5 minutes
|
|
189
|
+
contentType: 'application/json'
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### Subscription Identifiers
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
// Identify which subscription triggered a message
|
|
197
|
+
await MqttQuic.subscribe({
|
|
198
|
+
topic: 'sensors/+',
|
|
199
|
+
qos: 1,
|
|
200
|
+
subscriptionIdentifier: 1 // Unique ID for this subscription
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// When message arrives, you'll know which subscription matched
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### User Properties (Custom Metadata)
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
// Add custom metadata to messages
|
|
210
|
+
await MqttQuic.publish({
|
|
211
|
+
topic: 'events',
|
|
212
|
+
payload: JSON.stringify({ value: 42 }),
|
|
213
|
+
userProperties: [
|
|
214
|
+
{ name: 'source', value: 'mobile-app' },
|
|
215
|
+
{ name: 'version', value: '1.2.3' },
|
|
216
|
+
{ name: 'device-id', value: 'device-123' }
|
|
217
|
+
],
|
|
218
|
+
contentType: 'application/json'
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
#### Response Topic & Correlation Data
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
// Request-response pattern
|
|
226
|
+
await MqttQuic.publish({
|
|
227
|
+
topic: 'request/data',
|
|
228
|
+
payload: 'request-id-123',
|
|
229
|
+
responseTopic: 'response/data', // Where to send response
|
|
230
|
+
correlationData: 'correlation-id-456' // Match request/response
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## MQTT 5.0 Features Summary
|
|
235
|
+
|
|
236
|
+
| Feature | Description | Use Case |
|
|
237
|
+
|---------|-------------|----------|
|
|
238
|
+
| **Session Expiry** | Control session persistence | Resume sessions after reconnect |
|
|
239
|
+
| **Message Expiry** | Auto-expire undelivered messages | Time-sensitive data |
|
|
240
|
+
| **Subscription Identifiers** | Identify subscription source | Multi-subscription handling |
|
|
241
|
+
| **User Properties** | Custom key-value metadata | Tracing, versioning, routing |
|
|
242
|
+
| **Content Type** | Message format indicator | JSON, XML, binary, etc. |
|
|
243
|
+
| **Response Topic** | Request-response pattern | RPC over MQTT |
|
|
244
|
+
| **Reason Codes** | Detailed error information | Better debugging |
|
|
245
|
+
| **Topic Aliases** | Reduce bandwidth | High-frequency publishing |
|
|
246
|
+
|
|
247
|
+
See [MQTT5_IMPLEMENTATION_COMPLETE.md](./MQTT5_IMPLEMENTATION_COMPLETE.md) for full details.
|
|
248
|
+
|
|
249
|
+
## TypeScript Interface
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
interface MqttQuicConnectOptions {
|
|
253
|
+
host: string;
|
|
254
|
+
port: number;
|
|
255
|
+
clientId: string;
|
|
256
|
+
username?: string;
|
|
257
|
+
password?: string;
|
|
258
|
+
cleanSession?: boolean;
|
|
259
|
+
keepalive?: number;
|
|
260
|
+
caFile?: string;
|
|
261
|
+
caPath?: string;
|
|
262
|
+
// MQTT 5.0 options
|
|
263
|
+
protocolVersion?: '3.1.1' | '5.0' | 'auto';
|
|
264
|
+
sessionExpiryInterval?: number;
|
|
265
|
+
receiveMaximum?: number;
|
|
266
|
+
maximumPacketSize?: number;
|
|
267
|
+
topicAliasMaximum?: number;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
interface MqttQuicPublishOptions {
|
|
271
|
+
topic: string;
|
|
272
|
+
payload: string | Uint8Array;
|
|
273
|
+
qos?: 0 | 1 | 2;
|
|
274
|
+
retain?: boolean;
|
|
275
|
+
// MQTT 5.0 properties
|
|
276
|
+
messageExpiryInterval?: number;
|
|
277
|
+
contentType?: string;
|
|
278
|
+
responseTopic?: string;
|
|
279
|
+
correlationData?: string | Uint8Array;
|
|
280
|
+
userProperties?: Array<{ name: string; value: string }>;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
interface MqttQuicSubscribeOptions {
|
|
284
|
+
topic: string;
|
|
285
|
+
qos?: 0 | 1 | 2;
|
|
286
|
+
// MQTT 5.0
|
|
287
|
+
subscriptionIdentifier?: number;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
interface MqttQuicTestHarnessOptions {
|
|
291
|
+
host: string;
|
|
292
|
+
port?: number;
|
|
293
|
+
clientId?: string;
|
|
294
|
+
topic?: string;
|
|
295
|
+
payload?: string;
|
|
296
|
+
caFile?: string;
|
|
297
|
+
caPath?: string;
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## ngtcp2 Build (Phase 2) ⏳
|
|
302
|
+
|
|
303
|
+
**Current Status:** Real QUIC transport implemented using ngtcp2 + quictls (OpenSSL fork).
|
|
304
|
+
|
|
305
|
+
### Quick Build (Recommended)
|
|
306
|
+
|
|
307
|
+
Use the unified build script to build all native dependencies:
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
# Build for both iOS and Android
|
|
311
|
+
./build-native.sh
|
|
312
|
+
|
|
313
|
+
# Build only iOS
|
|
314
|
+
./build-native.sh --ios-only
|
|
315
|
+
|
|
316
|
+
# Build only Android
|
|
317
|
+
./build-native.sh --android-only
|
|
318
|
+
|
|
319
|
+
# Build for specific Android ABI
|
|
320
|
+
./build-native.sh --android-only --abi arm64-v8a
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
This script builds OpenSSL (quictls) → nghttp3 → ngtcp2 in the correct order for both platforms.
|
|
324
|
+
|
|
325
|
+
### Manual Build
|
|
326
|
+
|
|
327
|
+
For detailed manual build instructions, see:
|
|
328
|
+
- **iOS**: [ios/NGTCP2_BUILD_INSTRUCTIONS.md](./ios/NGTCP2_BUILD_INSTRUCTIONS.md)
|
|
329
|
+
- **Android**: [android/NGTCP2_BUILD_INSTRUCTIONS.md](./android/NGTCP2_BUILD_INSTRUCTIONS.md)
|
|
330
|
+
- **Full Plan**: [NGTCP2_INTEGRATION_PLAN.md](./NGTCP2_INTEGRATION_PLAN.md)
|
|
331
|
+
|
|
332
|
+
**Prerequisites:**
|
|
333
|
+
- iOS: macOS with Xcode 14+
|
|
334
|
+
- Android: Android Studio with NDK r25+ (auto-detected from `$ANDROID_HOME`)
|
|
335
|
+
|
|
336
|
+
## Development
|
|
337
|
+
|
|
338
|
+
### Build Plugin
|
|
339
|
+
|
|
340
|
+
```bash
|
|
341
|
+
cd production/capacitor-mqtt-quic
|
|
342
|
+
npm install
|
|
343
|
+
npm run build
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Add to Capacitor App
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
cd production/annadata-production
|
|
350
|
+
npm i @annadata/capacitor-mqtt-quic
|
|
351
|
+
npx cap sync
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Usage in App
|
|
355
|
+
|
|
356
|
+
```ts
|
|
357
|
+
import { MqttQuic } from '@annadata/capacitor-mqtt-quic';
|
|
358
|
+
import { MqttQuicService } from './services/MqttQuicService';
|
|
359
|
+
|
|
360
|
+
// Via service (recommended)
|
|
361
|
+
const mqttService = new MqttQuicService();
|
|
362
|
+
await mqttService.connect();
|
|
363
|
+
|
|
364
|
+
// Or directly
|
|
365
|
+
await MqttQuic.connect({
|
|
366
|
+
host: 'mqtt.annadata.cloud',
|
|
367
|
+
port: 1884,
|
|
368
|
+
clientId: 'device-123',
|
|
369
|
+
protocolVersion: '5.0'
|
|
370
|
+
});
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## Documentation
|
|
374
|
+
|
|
375
|
+
- [Implementation Summary](./IMPLEMENTATION_SUMMARY.md) - Complete project overview
|
|
376
|
+
- [MQTT 5.0 Implementation](./MQTT5_IMPLEMENTATION_COMPLETE.md) - MQTT 5.0 features and usage
|
|
377
|
+
- [ngtcp2 Integration Plan](./NGTCP2_INTEGRATION_PLAN.md) - Build instructions for real QUIC
|
|
378
|
+
- [MQTT Version Analysis](./MQTT_VERSION_ANALYSIS.md) - Why MQTT 5.0?
|
|
379
|
+
|
|
380
|
+
## Web/PWA Support
|
|
381
|
+
|
|
382
|
+
On **web** (including PWA), the plugin uses **MQTT over WebSocket (WSS)** via `mqtt.js`. No QUIC; same API.
|
|
383
|
+
|
|
384
|
+
- **Connect:** `ws://host:port` or `wss://host:port` (wss when port is 8884 or 443)
|
|
385
|
+
- **Build:** Ensure `mqtt` is installed (`npm install` in the plugin directory)
|
|
386
|
+
- Use `MqttQuic.connect` / `publish` / `subscribe` / `unsubscribe` / `disconnect` as on native
|
|
387
|
+
|
|
388
|
+
## Compatibility
|
|
389
|
+
|
|
390
|
+
- **MQTT Protocol:** 3.1.1 and 5.0 (auto-negotiation)
|
|
391
|
+
- **iOS:** 15.0+ (for Network framework)
|
|
392
|
+
- **Android:** API 21+ (Android 5.0+)
|
|
393
|
+
- **Web/PWA:** mqtt.js over WSS
|
|
394
|
+
- **Capacitor:** 7.0+
|
|
395
|
+
- **QUIC:** ngtcp2 1.21.0+ (when integrated)
|
|
396
|
+
|
|
397
|
+
## License
|
|
398
|
+
|
|
399
|
+
MIT
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# Building ngtcp2 for Android
|
|
2
|
+
|
|
3
|
+
This document provides instructions for building ngtcp2 and OpenSSL for Android to enable real QUIC transport in the MQTT client.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- **Android NDK r25+**
|
|
8
|
+
- **CMake 3.20+**
|
|
9
|
+
- **Android SDK 21+** (API level 21 = Android 5.0)
|
|
10
|
+
- **Git** (for cloning repositories)
|
|
11
|
+
|
|
12
|
+
**NDK path tip:** the build scripts auto-detect NDK installs in
|
|
13
|
+
`~/Library/Android/sdk/ndk` (macOS) or `~/Android/Sdk/ndk` (Linux). You can
|
|
14
|
+
omit `--ndk-path` if your NDK is installed in one of those locations.
|
|
15
|
+
|
|
16
|
+
**Source layout:** set `PROJECT_DIR` to the `capacitor-mqtt-quic` repo root.
|
|
17
|
+
Build scripts then expect dependencies under:
|
|
18
|
+
- `$PROJECT_DIR/ref-code/ngtcp2`
|
|
19
|
+
- `$PROJECT_DIR/ref-code/nghttp3`
|
|
20
|
+
- `$PROJECT_DIR/ref-code/openssl`
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
export PROJECT_DIR="/Users/annadata/Project_A/annadata-production/ref-code/capacitor-mqtt-quic"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Override with `NGTCP2_SOURCE_DIR`, `NGHTTP3_SOURCE_DIR`, `OPENSSL_SOURCE_DIR`
|
|
27
|
+
if you store sources elsewhere.
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
### Option 1: Use Pre-built Libraries (Recommended for Development)
|
|
32
|
+
|
|
33
|
+
If you have access to pre-built ngtcp2, nghttp3, and OpenSSL libraries:
|
|
34
|
+
|
|
35
|
+
1. Place `libngtcp2_client.so` in `android/src/main/jniLibs/<abi>/`
|
|
36
|
+
2. Place `libnghttp3.a` in `android/libs/<abi>/`
|
|
37
|
+
3. Place OpenSSL libraries (`libssl.a`, `libcrypto.a`) in `android/libs/<abi>/`
|
|
38
|
+
4. Update `build.gradle` and `CMakeLists.txt` to link against these libraries
|
|
39
|
+
|
|
40
|
+
### Option 2: Build from Source
|
|
41
|
+
|
|
42
|
+
#### Step 1: Build OpenSSL for Android (QUIC TLS)
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
cd android
|
|
46
|
+
./build-openssl.sh \
|
|
47
|
+
--ndk-path ~/Library/Android/sdk/ndk/<ndk-version> \
|
|
48
|
+
--abi arm64-v8a \
|
|
49
|
+
--platform android-21 \
|
|
50
|
+
--quictls
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This will:
|
|
54
|
+
- Clone quictls (OpenSSL fork with QUIC API) if not present
|
|
55
|
+
- Build static libraries for Android
|
|
56
|
+
- Install to `android/install/openssl-android/<abi>/`
|
|
57
|
+
|
|
58
|
+
**Note:** For different ABIs, repeat the build:
|
|
59
|
+
```bash
|
|
60
|
+
# arm64-v8a (64-bit ARM)
|
|
61
|
+
./build-openssl.sh --ndk-path ~/Android/Sdk/ndk/25.2.9519653 --platform android-21 --abi arm64-v8a --quictls
|
|
62
|
+
# ./build-openssl.sh --ndk-path "/Users/annadata/Library/Android/sdk/ndk/29.0.13113456" --abi arm64-v8a --platform android-21 --quictls
|
|
63
|
+
|
|
64
|
+
# armeabi-v7a (32-bit ARM)
|
|
65
|
+
./build-openssl.sh --ndk-path ~/Android/Sdk/ndk/25.2.9519653 --platform android-21 --abi armeabi-v7a --quictls
|
|
66
|
+
# ./build-openssl.sh --ndk-path "/Users/annadata/Library/Android/sdk/ndk/29.0.13113456" --abi armeabi-v7a --platform android-21 --quictls
|
|
67
|
+
|
|
68
|
+
# x86_64 (64-bit x86)
|
|
69
|
+
./build-openssl.sh --ndk-path ~/Android/Sdk/ndk/25.2.9519653 --platform android-21 --abi x86_64 --quictls
|
|
70
|
+
# ./build-openssl.sh --ndk-path "/Users/annadata/Library/Android/sdk/ndk/29.0.13113456" --abi x86_64 --platform android-21 --quictls
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### Step 2: Build nghttp3 for Android
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
cd android
|
|
77
|
+
./build-nghttp3.sh \
|
|
78
|
+
--ndk-path ~/Library/Android/sdk/ndk/<ndk-version> \
|
|
79
|
+
--abi arm64-v8a \
|
|
80
|
+
--platform android-21
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
This will:
|
|
84
|
+
- Build nghttp3 static library
|
|
85
|
+
- Install to `android/install/nghttp3-android/<abi>/`
|
|
86
|
+
|
|
87
|
+
- ./build-nghttp3.sh --abi arm64-v8a --platform android-21
|
|
88
|
+
|
|
89
|
+
#### Step 3: Build ngtcp2 for Android
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
cd android
|
|
93
|
+
./build-ngtcp2.sh \
|
|
94
|
+
--ndk-path ~/Library/Android/sdk/ndk/<ndk-version> \
|
|
95
|
+
--abi arm64-v8a \
|
|
96
|
+
--platform android-21 \
|
|
97
|
+
--openssl-path ./install/openssl-android/arm64-v8a \
|
|
98
|
+
--quictls
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
This will:
|
|
102
|
+
- Build ngtcp2 static library
|
|
103
|
+
- Link against OpenSSL
|
|
104
|
+
- Install to `android/install/ngtcp2-android/<abi>/`
|
|
105
|
+
./build-ngtcp2.sh --abi arm64-v8a --platform android-21 --openssl-path ./install/openssl-android --quictls
|
|
106
|
+
|
|
107
|
+
#### Step 4: Build JNI Library
|
|
108
|
+
|
|
109
|
+
The JNI wrapper (`ngtcp2_jni.cpp`) is built as part of the Android project using CMake.
|
|
110
|
+
|
|
111
|
+
Update `android/build.gradle`:
|
|
112
|
+
|
|
113
|
+
```gradle
|
|
114
|
+
android {
|
|
115
|
+
// ...
|
|
116
|
+
externalNativeBuild {
|
|
117
|
+
cmake {
|
|
118
|
+
path "src/main/cpp/CMakeLists.txt"
|
|
119
|
+
version "3.22.1"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
ndk {
|
|
124
|
+
abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86_64'
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Integration into Android Project
|
|
130
|
+
|
|
131
|
+
### Update build.gradle
|
|
132
|
+
|
|
133
|
+
Add CMake configuration:
|
|
134
|
+
|
|
135
|
+
```gradle
|
|
136
|
+
android {
|
|
137
|
+
compileSdkVersion 34
|
|
138
|
+
|
|
139
|
+
defaultConfig {
|
|
140
|
+
// ...
|
|
141
|
+
externalNativeBuild {
|
|
142
|
+
cmake {
|
|
143
|
+
arguments "-DANDROID_STL=c++_shared"
|
|
144
|
+
cppFlags "-std=c++17"
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
ndk {
|
|
148
|
+
abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86_64'
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
externalNativeBuild {
|
|
153
|
+
cmake {
|
|
154
|
+
path "src/main/cpp/CMakeLists.txt"
|
|
155
|
+
version "3.22.1"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Update CMakeLists.txt
|
|
162
|
+
|
|
163
|
+
Ensure `CMakeLists.txt` points to correct ngtcp2 and OpenSSL paths:
|
|
164
|
+
|
|
165
|
+
```cmake
|
|
166
|
+
# Set paths
|
|
167
|
+
set(NGTCP2_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../ngtcp2")
|
|
168
|
+
set(NGHTTP3_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../nghttp3")
|
|
169
|
+
set(OPENSSL_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../install/openssl-android")
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## TLS Certificate Verification (QUIC)
|
|
173
|
+
|
|
174
|
+
QUIC requires TLS 1.3 and certificate verification is **enabled by default**.
|
|
175
|
+
You can bundle a CA PEM and it will be loaded automatically:
|
|
176
|
+
|
|
177
|
+
- `android/src/main/assets/mqttquic_ca.pem`
|
|
178
|
+
|
|
179
|
+
You can also override per call:
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
await MqttQuic.connect({
|
|
183
|
+
host: 'mqtt.example.com',
|
|
184
|
+
port: 1884,
|
|
185
|
+
clientId: 'my-client-id',
|
|
186
|
+
caFile: '/path/to/ca-bundle.pem',
|
|
187
|
+
// or caPath: '/path/to/ca-directory'
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### How to generate certificates
|
|
192
|
+
|
|
193
|
+
**Option A: Public CA (Let’s Encrypt)**
|
|
194
|
+
You do not bundle `mqttquic_ca.pem` (the OS already trusts public CAs).
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
sudo apt-get update
|
|
198
|
+
sudo apt-get install -y certbot
|
|
199
|
+
sudo certbot certonly --standalone -d mqtt.example.com
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Use on server:
|
|
203
|
+
- Cert: `/etc/letsencrypt/live/mqtt.example.com/fullchain.pem`
|
|
204
|
+
- Key: `/etc/letsencrypt/live/mqtt.example.com/privkey.pem`
|
|
205
|
+
|
|
206
|
+
**Option B: Private CA (dev/internal)**
|
|
207
|
+
Generate your own CA, sign the server cert, and bundle the CA PEM.
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
mkdir -p certs && cd certs
|
|
211
|
+
|
|
212
|
+
# 1) Create CA (one-time)
|
|
213
|
+
openssl genrsa -out ca.key 4096
|
|
214
|
+
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.pem \
|
|
215
|
+
-subj "/C=US/ST=CA/L=SF/O=Annadata/OU=MQTT/CN=Annadata-Root-CA"
|
|
216
|
+
|
|
217
|
+
# 2) Create server key + CSR
|
|
218
|
+
openssl genrsa -out server.key 2048
|
|
219
|
+
openssl req -new -key server.key -out server.csr \
|
|
220
|
+
-subj "/C=US/ST=CA/L=SF/O=Annadata/OU=MQTT/CN=mqtt.example.com"
|
|
221
|
+
|
|
222
|
+
# 3) Add SANs (edit DNS/IP)
|
|
223
|
+
cat > server_ext.cnf <<EOF
|
|
224
|
+
subjectAltName = DNS:mqtt.example.com,IP:YOUR.SERVER.IP
|
|
225
|
+
extendedKeyUsage = serverAuth
|
|
226
|
+
EOF
|
|
227
|
+
|
|
228
|
+
# 4) Sign server cert
|
|
229
|
+
openssl x509 -req -in server.csr -CA ca.pem -CAkey ca.key -CAcreateserial \
|
|
230
|
+
-out server.pem -days 365 -sha256 -extfile server_ext.cnf
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Bundle the CA cert (never ship `ca.key`):
|
|
234
|
+
- Android: `android/src/main/assets/mqttquic_ca.pem` (use `ca.pem`)
|
|
235
|
+
|
|
236
|
+
## Test Harness (QUIC Smoke Test)
|
|
237
|
+
|
|
238
|
+
This runs: connect → subscribe → publish → disconnect.
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
await MqttQuic.testHarness({
|
|
242
|
+
host: 'mqtt.example.com',
|
|
243
|
+
port: 1884,
|
|
244
|
+
clientId: 'mqttquic_test_client',
|
|
245
|
+
topic: 'test/topic',
|
|
246
|
+
payload: 'Hello QUIC!',
|
|
247
|
+
// optional CA override
|
|
248
|
+
caFile: '/path/to/ca-bundle.pem'
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Using Pre-built Libraries
|
|
253
|
+
|
|
254
|
+
### OpenSSL
|
|
255
|
+
|
|
256
|
+
Download from:
|
|
257
|
+
- https://github.com/leenjewel/openssl_for_ios_and_android
|
|
258
|
+
|
|
259
|
+
### ngtcp2
|
|
260
|
+
|
|
261
|
+
Currently, there are no widely available pre-built ngtcp2 libraries for Android. You'll need to build from source.
|
|
262
|
+
|
|
263
|
+
### nghttp3
|
|
264
|
+
|
|
265
|
+
Currently, there are no widely available pre-built nghttp3 libraries for Android. You'll need to build from source. Make sure you clone with submodules:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
git clone --recurse-submodules https://github.com/ngtcp2/nghttp3.git
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Troubleshooting
|
|
272
|
+
|
|
273
|
+
### NDK Not Found
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
# Install via Android Studio SDK Manager
|
|
277
|
+
# Or download from: https://developer.android.com/ndk/downloads
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### CMake Not Found
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
# Install via Android Studio SDK Manager
|
|
284
|
+
# Or download from: https://cmake.org/download/
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### OpenSSL Build Fails
|
|
288
|
+
|
|
289
|
+
- Ensure you're using OpenSSL 3.0+ (required for TLS 1.3)
|
|
290
|
+
- Check that NDK path is correct
|
|
291
|
+
- Verify ABI is supported (arm64-v8a, armeabi-v7a, x86_64, x86)
|
|
292
|
+
|
|
293
|
+
### ngtcp2 Build Fails
|
|
294
|
+
|
|
295
|
+
- Verify OpenSSL is built and path is correct
|
|
296
|
+
- Check CMake version: `cmake --version` (must be 3.20+)
|
|
297
|
+
- Ensure Android platform matches (android-21+)
|
|
298
|
+
|
|
299
|
+
### Link Errors
|
|
300
|
+
|
|
301
|
+
- Verify all libraries are built for the same ABI
|
|
302
|
+
- Check that CMakeLists.txt paths are correct
|
|
303
|
+
- Ensure OpenSSL and ngtcp2 are linked in the correct order
|
|
304
|
+
|
|
305
|
+
### JNI Errors
|
|
306
|
+
|
|
307
|
+
- Verify native method names match exactly (package + class + method)
|
|
308
|
+
- Check that `System.loadLibrary("ngtcp2_client")` is called
|
|
309
|
+
- Ensure library is in correct ABI folder: `src/main/jniLibs/<abi>/`
|
|
310
|
+
|
|
311
|
+
## Next Steps
|
|
312
|
+
|
|
313
|
+
After building ngtcp2/nghttp3:
|
|
314
|
+
|
|
315
|
+
1. Update `ngtcp2_jni.cpp` to implement the TODO sections
|
|
316
|
+
2. Replace `QuicClientStub` with `NGTCP2Client` in `MQTTClient.kt`
|
|
317
|
+
3. Test connection to MQTT server over QUIC
|
|
318
|
+
|
|
319
|
+
See `NGTCP2_INTEGRATION_PLAN.md` for detailed implementation guide.
|