@seriousme/opifex 1.11.2 → 2.0.3
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/LICENSE.txt +1 -1
- package/README.md +102 -43
- package/bin/conformanceServer.ts +45 -13
- package/bin/demoServer.ts +38 -13
- package/bin/mqtt.ts +52 -30
- package/client/ConnectionState.ts +2 -0
- package/client/client.ts +57 -47
- package/client/context.ts +75 -21
- package/client/deps.ts +5 -0
- package/client/handlers/handleAuth.test.ts +193 -0
- package/client/handlers/handleAuth.ts +89 -0
- package/client/handlers/handleConnack.test.ts +173 -0
- package/client/handlers/handleConnack.ts +16 -6
- package/client/handlers/handleDisconnect.test.ts +22 -0
- package/client/handlers/handleDisconnect.ts +17 -0
- package/client/handlers/handlePacket.test.ts +242 -0
- package/client/handlers/handlePacket.ts +19 -5
- package/client/handlers/handlePuback.test.ts +99 -0
- package/client/handlers/handlePuback.ts +6 -0
- package/client/handlers/handlePubcomp.test.ts +112 -0
- package/client/handlers/handlePubcomp.ts +5 -0
- package/client/handlers/handlePublish.test.ts +188 -0
- package/client/handlers/handlePubrec.test.ts +119 -0
- package/client/handlers/handlePubrec.ts +5 -1
- package/client/handlers/handlePubrel.test.ts +136 -0
- package/client/handlers/handleSuback.test.ts +122 -0
- package/client/handlers/handleSuback.ts +3 -0
- package/client/handlers/handleUnsuback.test.ts +101 -0
- package/client/mod.ts +2 -2
- package/client/store/memoryStore.ts +2 -2
- package/client/store/store.ts +3 -1
- package/deno/README.md +1 -2
- package/deno/tcpClient.test.ts +75 -0
- package/deno/tcpClient.ts +16 -11
- package/deno/tcpServer.test.ts +22 -21
- package/deno/tcpServer.ts +5 -0
- package/deno/tlsServer.test.ts +97 -0
- package/deno/tlsServer.ts +42 -0
- package/deno/wsServer.test.ts +93 -0
- package/deno/wsServer.ts +73 -0
- package/dev_utils/generateCert.ts +95 -0
- package/dev_utils/mockConn.ts +36 -0
- package/dev_utils/mockServer.ts +28 -0
- package/dev_utils/mod.ts +30 -2
- package/dev_utils/packetHelpers.ts +404 -0
- package/dev_utils/test-handlers.ts +98 -0
- package/dev_utils/timers.ts +14 -0
- package/dev_utils/webStreamPair.test.ts +171 -0
- package/dev_utils/webStreamPair.ts +143 -0
- package/dist/bin/conformanceServer.js +17 -12
- package/dist/bin/conformanceServer.js.map +1 -1
- package/dist/bin/demoServer.js +16 -12
- package/dist/bin/demoServer.js.map +1 -1
- package/dist/bin/mqtt.js +19 -16
- package/dist/bin/mqtt.js.map +1 -1
- package/dist/client/ConnectionState.d.ts +2 -0
- package/dist/client/ConnectionState.d.ts.map +1 -1
- package/dist/client/ConnectionState.js +2 -0
- package/dist/client/ConnectionState.js.map +1 -1
- package/dist/client/client.d.ts +19 -22
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +34 -20
- package/dist/client/client.js.map +1 -1
- package/dist/client/context.d.ts +11 -3
- package/dist/client/context.d.ts.map +1 -1
- package/dist/client/context.js +44 -12
- package/dist/client/context.js.map +1 -1
- package/dist/client/deps.d.ts +3 -2
- package/dist/client/deps.d.ts.map +1 -1
- package/dist/client/deps.js +1 -1
- package/dist/client/deps.js.map +1 -1
- package/dist/client/handlers/handleAuth.d.ts +9 -0
- package/dist/client/handlers/handleAuth.d.ts.map +1 -0
- package/dist/client/handlers/handleAuth.js +66 -0
- package/dist/client/handlers/handleAuth.js.map +1 -0
- package/dist/client/handlers/handleConnack.d.ts +2 -2
- package/dist/client/handlers/handleConnack.d.ts.map +1 -1
- package/dist/client/handlers/handleConnack.js +16 -6
- package/dist/client/handlers/handleConnack.js.map +1 -1
- package/dist/client/handlers/handleDisconnect.d.ts +11 -0
- package/dist/client/handlers/handleDisconnect.d.ts.map +1 -0
- package/dist/client/handlers/handleDisconnect.js +15 -0
- package/dist/client/handlers/handleDisconnect.js.map +1 -0
- package/dist/client/handlers/handlePacket.d.ts.map +1 -1
- package/dist/client/handlers/handlePacket.js +14 -3
- package/dist/client/handlers/handlePacket.js.map +1 -1
- package/dist/client/handlers/handlePuback.d.ts.map +1 -1
- package/dist/client/handlers/handlePuback.js +5 -0
- package/dist/client/handlers/handlePuback.js.map +1 -1
- package/dist/client/handlers/handlePubcomp.d.ts.map +1 -1
- package/dist/client/handlers/handlePubcomp.js +5 -0
- package/dist/client/handlers/handlePubcomp.js.map +1 -1
- package/dist/client/handlers/handlePubrec.d.ts.map +1 -1
- package/dist/client/handlers/handlePubrec.js +5 -1
- package/dist/client/handlers/handlePubrec.js.map +1 -1
- package/dist/client/handlers/handleSuback.d.ts +1 -0
- package/dist/client/handlers/handleSuback.d.ts.map +1 -1
- package/dist/client/handlers/handleSuback.js +3 -0
- package/dist/client/handlers/handleSuback.js.map +1 -1
- package/dist/client/mod.d.ts +2 -2
- package/dist/client/mod.d.ts.map +1 -1
- package/dist/client/mod.js +1 -1
- package/dist/client/mod.js.map +1 -1
- package/dist/client/store/memoryStore.d.ts.map +1 -1
- package/dist/client/store/memoryStore.js +2 -2
- package/dist/client/store/memoryStore.js.map +1 -1
- package/dist/client/store/store.d.ts +2 -1
- package/dist/client/store/store.d.ts.map +1 -1
- package/dist/client/store/store.js +1 -1
- package/dist/client/store/store.js.map +1 -1
- package/dist/dev_utils/generateCert.d.ts +11 -0
- package/dist/dev_utils/generateCert.d.ts.map +1 -0
- package/dist/dev_utils/generateCert.js +76 -0
- package/dist/dev_utils/generateCert.js.map +1 -0
- package/dist/examples/authHandler.test.d.ts +2 -0
- package/dist/examples/authHandler.test.d.ts.map +1 -0
- package/dist/examples/authHandler.test.js +123 -0
- package/dist/examples/authHandler.test.js.map +1 -0
- package/dist/examples/mqtt.test.d.ts +2 -0
- package/dist/examples/mqtt.test.d.ts.map +1 -0
- package/dist/examples/mqtt.test.js +70 -0
- package/dist/examples/mqtt.test.js.map +1 -0
- package/dist/examples/mqtts.test.d.ts +2 -0
- package/dist/examples/mqtts.test.d.ts.map +1 -0
- package/dist/examples/mqtts.test.js +81 -0
- package/dist/examples/mqtts.test.js.map +1 -0
- package/dist/mqttConn/mqttConn.d.ts +25 -16
- package/dist/mqttConn/mqttConn.d.ts.map +1 -1
- package/dist/mqttConn/mqttConn.js +69 -40
- package/dist/mqttConn/mqttConn.js.map +1 -1
- package/dist/mqttPacket/AuthenticationResult.d.ts.map +1 -1
- package/dist/mqttPacket/BitMask.d.ts +12 -11
- package/dist/mqttPacket/BitMask.d.ts.map +1 -1
- package/dist/mqttPacket/BitMask.js +10 -18
- package/dist/mqttPacket/BitMask.js.map +1 -1
- package/dist/mqttPacket/PacketType.d.ts.map +1 -1
- package/dist/mqttPacket/Properties.d.ts +2 -2
- package/dist/mqttPacket/Properties.d.ts.map +1 -1
- package/dist/mqttPacket/Properties.js.map +1 -1
- package/dist/mqttPacket/ReasonCode.d.ts +1 -0
- package/dist/mqttPacket/ReasonCode.d.ts.map +1 -1
- package/dist/mqttPacket/ReasonCode.js +1 -0
- package/dist/mqttPacket/ReasonCode.js.map +1 -1
- package/dist/mqttPacket/RetainHandling.d.ts.map +1 -1
- package/dist/mqttPacket/auth.d.ts +3 -5
- package/dist/mqttPacket/auth.d.ts.map +1 -1
- package/dist/mqttPacket/auth.js +31 -30
- package/dist/mqttPacket/auth.js.map +1 -1
- package/dist/mqttPacket/codec.d.ts +48 -0
- package/dist/mqttPacket/codec.d.ts.map +1 -0
- package/dist/mqttPacket/codec.js +93 -0
- package/dist/mqttPacket/codec.js.map +1 -0
- package/dist/mqttPacket/connack.d.ts +4 -5
- package/dist/mqttPacket/connack.d.ts.map +1 -1
- package/dist/mqttPacket/connack.js +43 -45
- package/dist/mqttPacket/connack.js.map +1 -1
- package/dist/mqttPacket/connect.d.ts +26 -27
- package/dist/mqttPacket/connect.d.ts.map +1 -1
- package/dist/mqttPacket/connect.js +156 -157
- package/dist/mqttPacket/connect.js.map +1 -1
- package/dist/mqttPacket/decoder.d.ts +1 -13
- package/dist/mqttPacket/decoder.d.ts.map +1 -1
- package/dist/mqttPacket/decoder.js +7 -28
- package/dist/mqttPacket/decoder.js.map +1 -1
- package/dist/mqttPacket/disconnect.d.ts +4 -5
- package/dist/mqttPacket/disconnect.d.ts.map +1 -1
- package/dist/mqttPacket/disconnect.js +41 -43
- package/dist/mqttPacket/disconnect.js.map +1 -1
- package/dist/mqttPacket/encoder.d.ts +1 -15
- package/dist/mqttPacket/encoder.d.ts.map +1 -1
- package/dist/mqttPacket/encoder.js +4 -27
- package/dist/mqttPacket/encoder.js.map +1 -1
- package/dist/mqttPacket/length.js +1 -1
- package/dist/mqttPacket/length.js.map +1 -1
- package/dist/mqttPacket/mod.d.ts +21 -103
- package/dist/mqttPacket/mod.d.ts.map +1 -1
- package/dist/mqttPacket/mod.js +5 -94
- package/dist/mqttPacket/mod.js.map +1 -1
- package/dist/mqttPacket/pingreq.d.ts +4 -5
- package/dist/mqttPacket/pingreq.d.ts.map +1 -1
- package/dist/mqttPacket/pingreq.js +11 -13
- package/dist/mqttPacket/pingreq.js.map +1 -1
- package/dist/mqttPacket/pingres.d.ts +4 -5
- package/dist/mqttPacket/pingres.d.ts.map +1 -1
- package/dist/mqttPacket/pingres.js +11 -13
- package/dist/mqttPacket/pingres.js.map +1 -1
- package/dist/mqttPacket/protocolLevels.d.ts +11 -6
- package/dist/mqttPacket/protocolLevels.d.ts.map +1 -1
- package/dist/mqttPacket/protocolLevels.js +5 -1
- package/dist/mqttPacket/protocolLevels.js.map +1 -1
- package/dist/mqttPacket/pubblishAcks.d.ts +9 -10
- package/dist/mqttPacket/pubblishAcks.d.ts.map +1 -1
- package/dist/mqttPacket/pubblishAcks.js +43 -45
- package/dist/mqttPacket/pubblishAcks.js.map +1 -1
- package/dist/mqttPacket/publish.d.ts +14 -14
- package/dist/mqttPacket/publish.d.ts.map +1 -1
- package/dist/mqttPacket/publish.js +53 -55
- package/dist/mqttPacket/publish.js.map +1 -1
- package/dist/mqttPacket/suback.d.ts +4 -5
- package/dist/mqttPacket/suback.d.ts.map +1 -1
- package/dist/mqttPacket/suback.js +38 -40
- package/dist/mqttPacket/suback.js.map +1 -1
- package/dist/mqttPacket/subscribe.d.ts +36 -7
- package/dist/mqttPacket/subscribe.d.ts.map +1 -1
- package/dist/mqttPacket/subscribe.js +90 -89
- package/dist/mqttPacket/subscribe.js.map +1 -1
- package/dist/mqttPacket/types.d.ts +1 -8
- package/dist/mqttPacket/types.d.ts.map +1 -1
- package/dist/mqttPacket/unsuback.d.ts +4 -5
- package/dist/mqttPacket/unsuback.d.ts.map +1 -1
- package/dist/mqttPacket/unsuback.js +32 -34
- package/dist/mqttPacket/unsuback.js.map +1 -1
- package/dist/mqttPacket/unsubscribe.d.ts +23 -6
- package/dist/mqttPacket/unsubscribe.d.ts.map +1 -1
- package/dist/mqttPacket/unsubscribe.js +45 -44
- package/dist/mqttPacket/unsubscribe.js.map +1 -1
- package/dist/mqttPacket/validators.d.ts +4 -16
- package/dist/mqttPacket/validators.d.ts.map +1 -1
- package/dist/mqttPacket/validators.js +5 -26
- package/dist/mqttPacket/validators.js.map +1 -1
- package/dist/node/tcpClient.d.ts +1 -1
- package/dist/node/tcpClient.d.ts.map +1 -1
- package/dist/node/tcpClient.js +12 -7
- package/dist/node/tcpClient.js.map +1 -1
- package/dist/node/tcpServer.d.ts.map +1 -1
- package/dist/node/tcpServer.js +4 -0
- package/dist/node/tcpServer.js.map +1 -1
- package/dist/node/tlsServer.d.ts +19 -0
- package/dist/node/tlsServer.d.ts.map +1 -0
- package/dist/node/tlsServer.js +58 -0
- package/dist/node/tlsServer.js.map +1 -0
- package/dist/node/wrapNodeSocket.d.ts.map +1 -1
- package/dist/node/wrapNodeSocket.js +6 -28
- package/dist/node/wrapNodeSocket.js.map +1 -1
- package/dist/persistence/basicPersistence.d.ts +36 -0
- package/dist/persistence/basicPersistence.d.ts.map +1 -0
- package/dist/persistence/basicPersistence.js +306 -0
- package/dist/persistence/basicPersistence.js.map +1 -0
- package/dist/persistence/deps.d.ts +3 -2
- package/dist/persistence/deps.d.ts.map +1 -1
- package/dist/persistence/deps.js +2 -1
- package/dist/persistence/deps.js.map +1 -1
- package/dist/persistence/memory/memoryPersistence.d.ts +6 -23
- package/dist/persistence/memory/memoryPersistence.d.ts.map +1 -1
- package/dist/persistence/memory/memoryPersistence.js +12 -115
- package/dist/persistence/memory/memoryPersistence.js.map +1 -1
- package/dist/persistence/memory/memoryStorage.d.ts +45 -0
- package/dist/persistence/memory/memoryStorage.d.ts.map +1 -0
- package/dist/persistence/memory/memoryStorage.js +143 -0
- package/dist/persistence/memory/memoryStorage.js.map +1 -0
- package/dist/persistence/mod.d.ts +3 -3
- package/dist/persistence/mod.d.ts.map +1 -1
- package/dist/persistence/mod.js +6 -1
- package/dist/persistence/mod.js.map +1 -1
- package/dist/persistence/persistence.d.ts +44 -69
- package/dist/persistence/persistence.d.ts.map +1 -1
- package/dist/persistence/persistence.js +1 -2
- package/dist/persistence/persistence.js.map +1 -1
- package/dist/persistence/sqlite/sqliteDatabase.d.ts +46 -0
- package/dist/persistence/sqlite/sqliteDatabase.d.ts.map +1 -0
- package/dist/persistence/sqlite/sqliteDatabase.js +215 -0
- package/dist/persistence/sqlite/sqliteDatabase.js.map +1 -0
- package/dist/persistence/sqlite/sqlitePersistence.d.ts +16 -0
- package/dist/persistence/sqlite/sqlitePersistence.d.ts.map +1 -0
- package/dist/persistence/sqlite/sqlitePersistence.js +29 -0
- package/dist/persistence/sqlite/sqlitePersistence.js.map +1 -0
- package/dist/persistence/sqlite/sqliteStorage.d.ts +36 -0
- package/dist/persistence/sqlite/sqliteStorage.d.ts.map +1 -0
- package/dist/persistence/sqlite/sqliteStorage.js +194 -0
- package/dist/persistence/sqlite/sqliteStorage.js.map +1 -0
- package/dist/persistence/storage.d.ts +38 -0
- package/dist/persistence/storage.d.ts.map +1 -0
- package/dist/persistence/storage.js +5 -0
- package/dist/persistence/storage.js.map +1 -0
- package/dist/server/config.d.ts +47 -0
- package/dist/server/config.d.ts.map +1 -0
- package/dist/server/config.js +41 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/context.d.ts +198 -24
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/context.js +432 -53
- package/dist/server/context.js.map +1 -1
- package/dist/server/deps.d.ts +5 -6
- package/dist/server/deps.d.ts.map +1 -1
- package/dist/server/deps.js +2 -2
- package/dist/server/deps.js.map +1 -1
- package/dist/server/handlers/completeConnect.d.ts +8 -0
- package/dist/server/handlers/completeConnect.d.ts.map +1 -0
- package/dist/server/handlers/completeConnect.js +74 -0
- package/dist/server/handlers/completeConnect.js.map +1 -0
- package/dist/server/handlers/handleAuth.d.ts +9 -0
- package/dist/server/handlers/handleAuth.d.ts.map +1 -0
- package/dist/server/handlers/handleAuth.js +69 -0
- package/dist/server/handlers/handleAuth.js.map +1 -0
- package/dist/server/handlers/handleConnect.d.ts +2 -4
- package/dist/server/handlers/handleConnect.d.ts.map +1 -1
- package/dist/server/handlers/handleConnect.js +140 -67
- package/dist/server/handlers/handleConnect.js.map +1 -1
- package/dist/server/handlers/handleDisconnect.d.ts +1 -1
- package/dist/server/handlers/handleDisconnect.d.ts.map +1 -1
- package/dist/server/handlers/handleDisconnect.js +3 -3
- package/dist/server/handlers/handleDisconnect.js.map +1 -1
- package/dist/server/handlers/handlePacket.d.ts.map +1 -1
- package/dist/server/handlers/handlePacket.js +19 -6
- package/dist/server/handlers/handlePacket.js.map +1 -1
- package/dist/server/handlers/handlePuback.d.ts +1 -3
- package/dist/server/handlers/handlePuback.d.ts.map +1 -1
- package/dist/server/handlers/handlePuback.js +2 -8
- package/dist/server/handlers/handlePuback.js.map +1 -1
- package/dist/server/handlers/handlePubcomp.d.ts +1 -1
- package/dist/server/handlers/handlePubcomp.d.ts.map +1 -1
- package/dist/server/handlers/handlePubcomp.js +2 -4
- package/dist/server/handlers/handlePubcomp.js.map +1 -1
- package/dist/server/handlers/handlePublish.d.ts +1 -1
- package/dist/server/handlers/handlePublish.d.ts.map +1 -1
- package/dist/server/handlers/handlePublish.js +149 -45
- package/dist/server/handlers/handlePublish.js.map +1 -1
- package/dist/server/handlers/handlePubrec.d.ts.map +1 -1
- package/dist/server/handlers/handlePubrec.js +2 -3
- package/dist/server/handlers/handlePubrec.js.map +1 -1
- package/dist/server/handlers/handlePubrel.d.ts.map +1 -1
- package/dist/server/handlers/handlePubrel.js +6 -2
- package/dist/server/handlers/handlePubrel.js.map +1 -1
- package/dist/server/handlers/handleSubscribe.d.ts +1 -6
- package/dist/server/handlers/handleSubscribe.d.ts.map +1 -1
- package/dist/server/handlers/handleSubscribe.js +99 -38
- package/dist/server/handlers/handleSubscribe.js.map +1 -1
- package/dist/server/handlers/handleUnsubscribe.d.ts.map +1 -1
- package/dist/server/handlers/handleUnsubscribe.js +19 -4
- package/dist/server/handlers/handleUnsubscribe.js.map +1 -1
- package/dist/server/mod.d.ts +3 -3
- package/dist/server/mod.d.ts.map +1 -1
- package/dist/server/mod.js +1 -1
- package/dist/server/mod.js.map +1 -1
- package/dist/server/server.d.ts +29 -6
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +84 -15
- package/dist/server/server.js.map +1 -1
- package/dist/socket/socket.d.ts +11 -9
- package/dist/socket/socket.d.ts.map +1 -1
- package/dist/socket/socket.js +67 -15
- package/dist/socket/socket.js.map +1 -1
- package/dist/timer/timer.d.ts +32 -0
- package/dist/timer/timer.d.ts.map +1 -1
- package/dist/timer/timer.js +32 -0
- package/dist/timer/timer.js.map +1 -1
- package/dist/trie/trie.d.ts +10 -5
- package/dist/trie/trie.d.ts.map +1 -1
- package/dist/trie/trie.js +47 -17
- package/dist/trie/trie.js.map +1 -1
- package/dist/utils/assert.d.ts.map +1 -1
- package/dist/utils/{BufferedAsyncIterable.d.ts → bufferedAsyncIterable.d.ts} +1 -1
- package/dist/utils/bufferedAsyncIterable.d.ts.map +1 -0
- package/dist/utils/{BufferedAsyncIterable.js → bufferedAsyncIterable.js} +1 -1
- package/dist/utils/bufferedAsyncIterable.js.map +1 -0
- package/dist/utils/deferred.d.ts.map +1 -1
- package/dist/utils/deferred.js.map +1 -1
- package/dist/utils/getOrInsertPolyFill.d.ts +2 -0
- package/dist/utils/getOrInsertPolyFill.d.ts.map +1 -0
- package/dist/utils/getOrInsertPolyFill.js +12 -0
- package/dist/utils/getOrInsertPolyFill.js.map +1 -0
- package/dist/utils/logger.d.ts +40 -3
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +53 -7
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/mod.d.ts +6 -1
- package/dist/utils/mod.d.ts.map +1 -1
- package/dist/utils/mod.js +5 -1
- package/dist/utils/mod.js.map +1 -1
- package/dist/utils/outboundTopicAliasManager.d.ts +52 -0
- package/dist/utils/outboundTopicAliasManager.d.ts.map +1 -0
- package/dist/utils/outboundTopicAliasManager.js +74 -0
- package/dist/utils/outboundTopicAliasManager.js.map +1 -0
- package/dist/utils/queue.d.ts.map +1 -1
- package/dist/utils/topicFilter.d.ts +32 -0
- package/dist/utils/topicFilter.d.ts.map +1 -0
- package/dist/utils/topicFilter.js +52 -0
- package/dist/utils/topicFilter.js.map +1 -0
- package/dist/utils/utils.d.ts +2 -0
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +2 -0
- package/dist/utils/utils.js.map +1 -1
- package/dist/utils/validators.d.ts +34 -0
- package/dist/utils/validators.d.ts.map +1 -0
- package/dist/utils/validators.js +61 -0
- package/dist/utils/validators.js.map +1 -0
- package/dist/web/webSocketStream.d.ts +29 -0
- package/dist/web/webSocketStream.d.ts.map +1 -0
- package/dist/web/webSocketStream.js +69 -0
- package/dist/web/webSocketStream.js.map +1 -0
- package/dist/web/wrapWebSocket.d.ts +12 -0
- package/dist/web/wrapWebSocket.d.ts.map +1 -0
- package/dist/web/wrapWebSocket.js +41 -0
- package/dist/web/wrapWebSocket.js.map +1 -0
- package/dist/web/wsClient.d.ts +7 -0
- package/dist/web/wsClient.d.ts.map +1 -0
- package/dist/web/wsClient.js +22 -0
- package/dist/web/wsClient.js.map +1 -0
- package/dist/web/wsClient.test.d.ts +2 -0
- package/dist/web/wsClient.test.d.ts.map +1 -0
- package/dist/web/wsClient.test.js +20 -0
- package/dist/web/wsClient.test.js.map +1 -0
- package/examples/authHandler.test.ts +164 -0
- package/examples/mqtt.test.ts +89 -0
- package/examples/mqtts.test.ts +100 -0
- package/examples/webSocket.test.ts +94 -0
- package/mqttConn/mqttConn.test.ts +9 -9
- package/mqttConn/mqttConn.ts +91 -49
- package/mqttPacket/BitMask.ts +11 -19
- package/mqttPacket/Properties.ts +6 -3
- package/mqttPacket/ReasonCode.ts +2 -1
- package/mqttPacket/auth.test.ts +1 -1
- package/mqttPacket/auth.ts +41 -48
- package/mqttPacket/codec.ts +146 -0
- package/mqttPacket/connack.ts +57 -68
- package/mqttPacket/connect.test.ts +2 -2
- package/mqttPacket/connect.ts +197 -204
- package/mqttPacket/decoder.test.ts +0 -39
- package/mqttPacket/decoder.ts +9 -36
- package/mqttPacket/disconnect.ts +57 -68
- package/mqttPacket/encoder.ts +5 -40
- package/mqttPacket/length.ts +1 -1
- package/mqttPacket/mod.ts +48 -200
- package/mqttPacket/pingreq.ts +21 -27
- package/mqttPacket/pingres.ts +21 -27
- package/mqttPacket/protocolLevels.ts +12 -7
- package/mqttPacket/pubblishAcks.ts +68 -77
- package/mqttPacket/publish.ts +76 -83
- package/mqttPacket/suback.ts +48 -58
- package/mqttPacket/subscribe.ts +141 -117
- package/mqttPacket/types.ts +1 -9
- package/mqttPacket/unsuback.ts +46 -58
- package/mqttPacket/unsubscribe.ts +74 -63
- package/mqttPacket/validators.ts +5 -31
- package/node/socket-node-stream.test.ts +322 -0
- package/node/socket-node-stream.ts +174 -0
- package/node/tcpClient.test.ts +70 -0
- package/node/tcpClient.ts +13 -12
- package/node/tcpServer.test.ts +24 -20
- package/node/tcpServer.ts +6 -0
- package/node/tlsServer.test.ts +94 -0
- package/node/tlsServer.ts +81 -0
- package/node/wrapNodeSocket.ts +7 -30
- package/package.json +73 -45
- package/persistence/basicPersistence.ts +495 -0
- package/persistence/deps.ts +11 -1
- package/persistence/memory/memoryPersistence.ts +12 -152
- package/persistence/memory/memoryStorage.ts +232 -0
- package/persistence/mod.ts +4 -4
- package/persistence/persistence.test.ts +24 -0
- package/persistence/persistence.testing.ts +1324 -0
- package/persistence/persistence.ts +111 -67
- package/persistence/sqlite/sqliteDatabase.ts +294 -0
- package/persistence/sqlite/sqlitePersistence.test.ts +48 -0
- package/persistence/sqlite/sqlitePersistence.ts +36 -0
- package/persistence/sqlite/sqliteStorage.ts +324 -0
- package/persistence/storage.ts +84 -0
- package/server/config.ts +65 -0
- package/server/context.ts +658 -74
- package/server/deps.ts +25 -5
- package/server/handlers/completeConnect.ts +93 -0
- package/server/handlers/duplicate.test.ts +196 -0
- package/server/handlers/handleAuth.test.ts +225 -0
- package/server/handlers/handleAuth.ts +88 -0
- package/server/handlers/handleConnect.test.ts +589 -0
- package/server/handlers/handleConnect.ts +201 -86
- package/server/handlers/handleConnect.version.test.ts +108 -0
- package/server/handlers/handleDisconnect.test.ts +56 -0
- package/server/handlers/handleDisconnect.ts +3 -3
- package/server/handlers/handlePacket.test.ts +24 -0
- package/server/handlers/handlePacket.ts +30 -8
- package/server/handlers/handlePingreq.test.ts +29 -0
- package/server/handlers/handlePuback.test.ts +28 -0
- package/server/handlers/handlePuback.ts +5 -8
- package/server/handlers/handlePubcomp.test.ts +28 -0
- package/server/handlers/handlePubcomp.ts +5 -4
- package/server/handlers/handlePublish.test.ts +471 -0
- package/server/handlers/handlePublish.ts +189 -35
- package/server/handlers/handlePubrec.test.ts +28 -0
- package/server/handlers/handlePubrec.ts +2 -3
- package/server/handlers/handlePubrel.test.ts +74 -0
- package/server/handlers/handlePubrel.ts +12 -2
- package/server/handlers/handleSubscribe.test.ts +971 -0
- package/server/handlers/handleSubscribe.ts +158 -39
- package/server/handlers/handleUnsubscribe.test.ts +79 -0
- package/server/handlers/handleUnsubscribe.ts +29 -5
- package/server/handlers/keepalive.test.ts +132 -0
- package/server/handlers/session.persistence.test.ts +194 -0
- package/server/handlers/will.delivery.test.ts +221 -0
- package/server/mod.ts +4 -3
- package/server/preconnect.test.ts +151 -0
- package/server/server.ts +115 -17
- package/socket/socket.test.ts +91 -0
- package/socket/socket.ts +81 -31
- package/timer/timer.test.ts +4 -7
- package/timer/timer.ts +36 -0
- package/tools/benchmarks/receiver.ts +51 -0
- package/tools/benchmarks/sender.ts +48 -0
- package/tools/benchmarks/utils.ts +31 -0
- package/tools/mqtt-compat/README.md +130 -0
- package/tools/mqtt-compat/broker.ts +52 -0
- package/tools/mqtt-compat/paho-mqtt4.test.ts +379 -0
- package/tools/mqtt-compat/paho-mqtt5.tests.ts +887 -0
- package/trie/trie.test.ts +9 -1
- package/trie/trie.ts +57 -22
- package/utils/bufferedAsyncIterable.test.ts +92 -0
- package/utils/{BufferedAsyncIterable.ts → bufferedAsyncIterable.ts} +1 -1
- package/utils/deferred.test.ts +36 -0
- package/utils/deferred.ts +0 -1
- package/utils/getOrInsertPolyFill.ts +12 -0
- package/utils/logger.test.ts +189 -0
- package/utils/logger.ts +62 -7
- package/utils/mod.ts +15 -1
- package/utils/outboundTopicAliasManager.ts +100 -0
- package/utils/queue.test.ts +61 -0
- package/utils/topicFilter.test.ts +63 -0
- package/utils/topicFilter.ts +61 -0
- package/utils/utils.ts +3 -0
- package/utils/validators.ts +67 -0
- package/web/webSocketStream.ts +97 -0
- package/web/wrapWebSocket.ts +52 -0
- package/web/wsClient.test.ts +26 -0
- package/web/wsClient.ts +25 -0
- package/benchmarks/receiver.ts +0 -35
- package/benchmarks/sender.ts +0 -32
- package/benchmarks/utils.ts +0 -9
- package/dev_utils/dummyConn.ts +0 -110
- package/dist/persistence/store.d.ts +0 -13
- package/dist/persistence/store.d.ts.map +0 -1
- package/dist/persistence/store.js +0 -2
- package/dist/persistence/store.js.map +0 -1
- package/dist/utils/BufferedAsyncIterable.d.ts.map +0 -1
- package/dist/utils/BufferedAsyncIterable.js.map +0 -1
- package/examples/tcpServer.test.ts +0 -148
- package/persistence/memory/memoryPersistence.test.ts +0 -156
- package/persistence/store.ts +0 -15
- package/server/test/handleConnect.test.ts +0 -130
- package/server/test/test-handlers.ts +0 -53
|
@@ -0,0 +1,971 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
import type { PublishPacket } from "../deps.ts";
|
|
4
|
+
import { PacketType, ReasonCode } from "../deps.ts";
|
|
5
|
+
import {
|
|
6
|
+
addMockClient,
|
|
7
|
+
connect,
|
|
8
|
+
connect5,
|
|
9
|
+
disconnect,
|
|
10
|
+
disconnect5,
|
|
11
|
+
isAuthenticatedBroker,
|
|
12
|
+
ping,
|
|
13
|
+
publish,
|
|
14
|
+
publish5,
|
|
15
|
+
startMockServer,
|
|
16
|
+
subscribe,
|
|
17
|
+
subscribe5,
|
|
18
|
+
} from "../../dev_utils/mod.ts";
|
|
19
|
+
import { SqlitePersistence } from "../../persistence/sqlite/sqlitePersistence.ts";
|
|
20
|
+
import { receiveMessages } from "../../dev_utils/packetHelpers.ts";
|
|
21
|
+
|
|
22
|
+
const txtEncoder = new TextEncoder();
|
|
23
|
+
|
|
24
|
+
test("SUBSCRIBE returns SUBACK with matching return codes", async () => {
|
|
25
|
+
const { mqttConn } = startMockServer();
|
|
26
|
+
|
|
27
|
+
// Connect first
|
|
28
|
+
await connect(mqttConn);
|
|
29
|
+
|
|
30
|
+
// Subscribe to a topic
|
|
31
|
+
// subscribe() also checks packetId and QoS in subacks
|
|
32
|
+
await subscribe(mqttConn, [{ topicFilter: "test/topic", qos: 0 }], { id: 1 });
|
|
33
|
+
await disconnect(mqttConn);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("SUBSCRIBE with multiple topics returns multiple return codes", async () => {
|
|
37
|
+
const { mqttConn } = startMockServer();
|
|
38
|
+
|
|
39
|
+
await connect(mqttConn);
|
|
40
|
+
// subscribe() also checks packetId and QoS in subacks
|
|
41
|
+
await subscribe(mqttConn, [
|
|
42
|
+
{ topicFilter: "topic/one", qos: 0 },
|
|
43
|
+
{ topicFilter: "topic/two", qos: 1 },
|
|
44
|
+
{ topicFilter: "topic/three", qos: 2 },
|
|
45
|
+
], { id: 2 });
|
|
46
|
+
|
|
47
|
+
await disconnect(mqttConn);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("SUBSCRIBE with wildcard topics works", async () => {
|
|
51
|
+
const { mqttConn } = startMockServer();
|
|
52
|
+
|
|
53
|
+
await connect(mqttConn);
|
|
54
|
+
|
|
55
|
+
await subscribe(mqttConn, [
|
|
56
|
+
{ topicFilter: "sensors/+/temperature", qos: 0 },
|
|
57
|
+
{ topicFilter: "events/#", qos: 1 },
|
|
58
|
+
], { id: 3 });
|
|
59
|
+
|
|
60
|
+
await disconnect(mqttConn);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("SUBSCRIBE with missing isAuthorizedToSubscribe handler authorizes subscribe", async () => {
|
|
64
|
+
const { mqttConn, mqttServer } = startMockServer();
|
|
65
|
+
mqttServer.handlers.isAuthorizedToSubscribe = undefined;
|
|
66
|
+
|
|
67
|
+
await connect(mqttConn);
|
|
68
|
+
|
|
69
|
+
await subscribe(mqttConn, [
|
|
70
|
+
{ topicFilter: "sensors/temperature", qos: 0 },
|
|
71
|
+
], { id: 3 });
|
|
72
|
+
|
|
73
|
+
await disconnect(mqttConn);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("SUBSCRIBE to unauthorized topic is rejected", async () => {
|
|
77
|
+
const { mqttConn } = startMockServer();
|
|
78
|
+
|
|
79
|
+
await connect(mqttConn);
|
|
80
|
+
|
|
81
|
+
const subAck = await subscribe(mqttConn, [
|
|
82
|
+
{ topicFilter: "topic/unauthorized", qos: 0 },
|
|
83
|
+
], { id: 3, checkAcks: false });
|
|
84
|
+
|
|
85
|
+
assert.deepStrictEqual(subAck.type, PacketType.suback, "Expected SUBACK");
|
|
86
|
+
assert.deepStrictEqual(subAck.id, 3);
|
|
87
|
+
assert.deepStrictEqual(
|
|
88
|
+
subAck.returnCodes,
|
|
89
|
+
[128],
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
await disconnect(mqttConn);
|
|
93
|
+
});
|
|
94
|
+
// ============================================================================
|
|
95
|
+
// Retained Message Tests
|
|
96
|
+
// ============================================================================
|
|
97
|
+
|
|
98
|
+
test("SUBSCRIBE receives retained message after SUBACK", async () => {
|
|
99
|
+
const { mqttConn: mqttConn1, mqttServer } = startMockServer();
|
|
100
|
+
const retainedPayload = "retained-value";
|
|
101
|
+
// First, publish a retained message (before any subscriber)
|
|
102
|
+
await connect(mqttConn1);
|
|
103
|
+
|
|
104
|
+
await publish(mqttConn1, "sensors/temperature", 0, {
|
|
105
|
+
payload: retainedPayload,
|
|
106
|
+
retain: true,
|
|
107
|
+
});
|
|
108
|
+
await disconnect(mqttConn1);
|
|
109
|
+
|
|
110
|
+
// Connect
|
|
111
|
+
const mqttConn2 = addMockClient(mqttServer);
|
|
112
|
+
await connect(mqttConn2);
|
|
113
|
+
|
|
114
|
+
// Subscribe to the topic with retained message
|
|
115
|
+
await subscribe(mqttConn2, [{ topicFilter: "sensors/temperature", qos: 0 }], {
|
|
116
|
+
id: 10,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Then should receive the retained message
|
|
120
|
+
const { value: publishPkt } = await mqttConn2.next();
|
|
121
|
+
assert.deepStrictEqual(
|
|
122
|
+
publishPkt.type,
|
|
123
|
+
PacketType.publish,
|
|
124
|
+
"Expected retained PUBLISH",
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
assert.deepStrictEqual(publishPkt.topic, "sensors/temperature");
|
|
128
|
+
assert.deepStrictEqual(
|
|
129
|
+
publishPkt.payload,
|
|
130
|
+
txtEncoder.encode(retainedPayload),
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
await disconnect(mqttConn2);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("SUBSCRIBE receives multiple retained messages matching wildcard", async () => {
|
|
137
|
+
const { mqttConn: mqttConn1, mqttServer } = startMockServer();
|
|
138
|
+
// Set up multiple retained messages
|
|
139
|
+
await connect(mqttConn1);
|
|
140
|
+
await publish(mqttConn1, "sensors/temp/living", 0, {
|
|
141
|
+
payload: "22",
|
|
142
|
+
retain: true,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
await publish(mqttConn1, "sensors/temp/bedroom", 0, {
|
|
146
|
+
payload: "20",
|
|
147
|
+
retain: true,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
await disconnect(mqttConn1);
|
|
151
|
+
|
|
152
|
+
// Connect the second client
|
|
153
|
+
const mqttConn2 = addMockClient(mqttServer);
|
|
154
|
+
await connect(mqttConn2);
|
|
155
|
+
|
|
156
|
+
// Subscribe with wildcard
|
|
157
|
+
await subscribe(mqttConn2, [{ topicFilter: "sensors/temp/#", qos: 0 }], {
|
|
158
|
+
id: 11,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Should receive both retained messages
|
|
162
|
+
const messages = await receiveMessages(mqttConn2);
|
|
163
|
+
|
|
164
|
+
const topics = messages
|
|
165
|
+
.filter((m): m is PublishPacket => m.type === PacketType.publish)
|
|
166
|
+
.map((m) => m.topic)
|
|
167
|
+
.sort();
|
|
168
|
+
|
|
169
|
+
assert.deepStrictEqual(topics, [
|
|
170
|
+
"sensors/temp/bedroom",
|
|
171
|
+
"sensors/temp/living",
|
|
172
|
+
]);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("SUBSCRIBE receives multiple retained messages with different QoS", async () => {
|
|
176
|
+
const { mqttConn: mqttConn1, mqttServer } = startMockServer();
|
|
177
|
+
// Set up multiple retained messages
|
|
178
|
+
await connect(mqttConn1);
|
|
179
|
+
await publish(mqttConn1, "retained/qos0", 0, {
|
|
180
|
+
retain: true,
|
|
181
|
+
id: undefined,
|
|
182
|
+
});
|
|
183
|
+
await publish(mqttConn1, "retained/qos1", 1, {
|
|
184
|
+
retain: true,
|
|
185
|
+
id: 10,
|
|
186
|
+
});
|
|
187
|
+
await publish(mqttConn1, "retained/qos2", 2, {
|
|
188
|
+
retain: true,
|
|
189
|
+
id: 11,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Subscribe
|
|
193
|
+
await subscribe(mqttConn1, [
|
|
194
|
+
{ topicFilter: "+/+", qos: 2 },
|
|
195
|
+
]);
|
|
196
|
+
|
|
197
|
+
// Should receive three retained messages
|
|
198
|
+
const messages = await receiveMessages(mqttConn1);
|
|
199
|
+
|
|
200
|
+
const topics = messages
|
|
201
|
+
.filter((m): m is PublishPacket => m.type === PacketType.publish)
|
|
202
|
+
.map((m) => m.topic)
|
|
203
|
+
.sort();
|
|
204
|
+
|
|
205
|
+
assert.deepStrictEqual(topics, [
|
|
206
|
+
"retained/qos0",
|
|
207
|
+
"retained/qos1",
|
|
208
|
+
"retained/qos2",
|
|
209
|
+
]);
|
|
210
|
+
|
|
211
|
+
const mqttConn2 = addMockClient(mqttServer);
|
|
212
|
+
await connect(mqttConn2);
|
|
213
|
+
// clear retained
|
|
214
|
+
await publish(mqttConn2, "retained/qos0", 0, {
|
|
215
|
+
payload: "",
|
|
216
|
+
retain: true,
|
|
217
|
+
id: undefined,
|
|
218
|
+
});
|
|
219
|
+
await publish(mqttConn2, "retained/qos1", 1, {
|
|
220
|
+
payload: "",
|
|
221
|
+
retain: true,
|
|
222
|
+
id: 10,
|
|
223
|
+
});
|
|
224
|
+
await publish(mqttConn2, "retained/qos2", 2, {
|
|
225
|
+
payload: "",
|
|
226
|
+
retain: true,
|
|
227
|
+
id: 11,
|
|
228
|
+
});
|
|
229
|
+
// Subscribe
|
|
230
|
+
await subscribe(mqttConn2, [
|
|
231
|
+
{ topicFilter: "+/+", qos: 2 },
|
|
232
|
+
]);
|
|
233
|
+
// should receive no messages, check with ping
|
|
234
|
+
await ping(mqttConn2);
|
|
235
|
+
await disconnect(mqttConn2);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test("SUBSCRIBE to topic without retained message receives only SUBACK", async () => {
|
|
239
|
+
const { mqttConn } = startMockServer();
|
|
240
|
+
|
|
241
|
+
await connect(mqttConn);
|
|
242
|
+
|
|
243
|
+
// Subscribe to topic with no retained message
|
|
244
|
+
await subscribe(mqttConn, [{ topicFilter: "no/retained/here", qos: 0 }]);
|
|
245
|
+
|
|
246
|
+
// No more messages should be pending - send a ping to verify
|
|
247
|
+
await ping(mqttConn);
|
|
248
|
+
await disconnect(mqttConn);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test("SUBSCRIBE with wildcard does not match $ topics", async () => {
|
|
252
|
+
const { mqttConn: subscriber, mqttServer } = startMockServer();
|
|
253
|
+
mqttServer.handlers.isAuthenticated = isAuthenticatedBroker;
|
|
254
|
+
|
|
255
|
+
const wildcardTopic = "+/+";
|
|
256
|
+
const dollarTopic = "$TopicA/B";
|
|
257
|
+
|
|
258
|
+
// subscribe to wildcard topic
|
|
259
|
+
await connect(subscriber);
|
|
260
|
+
await subscribe(subscriber, [{ topicFilter: wildcardTopic, qos: 1 }], {
|
|
261
|
+
id: 30,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const publisher = addMockClient(mqttServer);
|
|
265
|
+
await connect(publisher);
|
|
266
|
+
|
|
267
|
+
await publish(publisher, dollarTopic, 1, {
|
|
268
|
+
payload: "dollar topic message",
|
|
269
|
+
retain: false,
|
|
270
|
+
id: 105,
|
|
271
|
+
});
|
|
272
|
+
await disconnect(publisher);
|
|
273
|
+
|
|
274
|
+
await ping(subscriber);
|
|
275
|
+
|
|
276
|
+
await disconnect(subscriber);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test("SUBSCRIBE receives retained messages and clearing works", async () => {
|
|
280
|
+
const { mqttConn: publisher1, mqttServer } = startMockServer();
|
|
281
|
+
|
|
282
|
+
const qos0topic = "fromb/qos 0";
|
|
283
|
+
const qos1topic = "fromb/qos 1";
|
|
284
|
+
const qos2topic = "fromb/qos2";
|
|
285
|
+
const wildcardTopic = "fromb/+";
|
|
286
|
+
|
|
287
|
+
await connect(publisher1);
|
|
288
|
+
|
|
289
|
+
await publish(publisher1, qos0topic, 0, {
|
|
290
|
+
payload: "qos0",
|
|
291
|
+
retain: true,
|
|
292
|
+
});
|
|
293
|
+
await publish(publisher1, qos1topic, 1, {
|
|
294
|
+
payload: "qos1",
|
|
295
|
+
retain: true,
|
|
296
|
+
id: 101,
|
|
297
|
+
});
|
|
298
|
+
await publish(publisher1, qos2topic, 2, {
|
|
299
|
+
payload: "qos2",
|
|
300
|
+
retain: true,
|
|
301
|
+
id: 102,
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
disconnect(publisher1);
|
|
305
|
+
|
|
306
|
+
const subscriber1 = addMockClient(mqttServer);
|
|
307
|
+
await connect(subscriber1);
|
|
308
|
+
|
|
309
|
+
await subscribe(subscriber1, [{ topicFilter: wildcardTopic, qos: 2 }], {
|
|
310
|
+
id: 20,
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const messages = await receiveMessages(subscriber1);
|
|
314
|
+
|
|
315
|
+
const receivedTopics = messages
|
|
316
|
+
.filter((m): m is PublishPacket => m.type === PacketType.publish)
|
|
317
|
+
.map((m) => m.topic)
|
|
318
|
+
.sort();
|
|
319
|
+
|
|
320
|
+
assert.deepStrictEqual(
|
|
321
|
+
receivedTopics,
|
|
322
|
+
[qos0topic, qos1topic, qos2topic].sort(),
|
|
323
|
+
"Should receive all three retained messages",
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
const publisher2 = addMockClient(mqttServer);
|
|
327
|
+
await connect(publisher2);
|
|
328
|
+
|
|
329
|
+
await publish(publisher2, qos0topic, 0, {
|
|
330
|
+
payload: "",
|
|
331
|
+
retain: true,
|
|
332
|
+
});
|
|
333
|
+
await publish(publisher2, qos1topic, 1, {
|
|
334
|
+
payload: "",
|
|
335
|
+
retain: true,
|
|
336
|
+
id: 103,
|
|
337
|
+
});
|
|
338
|
+
await publish(publisher2, qos2topic, 2, {
|
|
339
|
+
payload: "",
|
|
340
|
+
retain: true,
|
|
341
|
+
id: 104,
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
await disconnect(publisher2);
|
|
345
|
+
|
|
346
|
+
const subscriber2 = addMockClient(mqttServer);
|
|
347
|
+
|
|
348
|
+
await connect(subscriber2);
|
|
349
|
+
|
|
350
|
+
await subscribe(subscriber2, [{ topicFilter: wildcardTopic, qos: 2 }], {
|
|
351
|
+
id: 21,
|
|
352
|
+
});
|
|
353
|
+
await ping(subscriber2);
|
|
354
|
+
|
|
355
|
+
await disconnect(subscriber2);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
test("SUBSCRIBE redelivery on reconnect (uncompleted QoS 1/2 exchanges)", async () => {
|
|
359
|
+
const sqlitePersistence = new SqlitePersistence();
|
|
360
|
+
const { mqttConn: subscriber, mqttServer } = startMockServer({
|
|
361
|
+
persistence: sqlitePersistence,
|
|
362
|
+
});
|
|
363
|
+
const clientId = "myclient";
|
|
364
|
+
const topic1 = "TopicA/B";
|
|
365
|
+
const topic3 = "TopicA/C";
|
|
366
|
+
const wildtopic6 = "TopicA/#";
|
|
367
|
+
|
|
368
|
+
await connect(subscriber, { clean: false, clientId });
|
|
369
|
+
await subscribe(subscriber, [{ topicFilter: wildtopic6, qos: 2 }], {
|
|
370
|
+
id: 40,
|
|
371
|
+
});
|
|
372
|
+
await disconnect(subscriber);
|
|
373
|
+
|
|
374
|
+
const publisher = addMockClient(mqttServer);
|
|
375
|
+
await connect(publisher);
|
|
376
|
+
|
|
377
|
+
await publish(publisher, topic1, 1, {
|
|
378
|
+
payload: "qos 1 message",
|
|
379
|
+
retain: false,
|
|
380
|
+
id: 201,
|
|
381
|
+
});
|
|
382
|
+
await publish(publisher, topic3, 2, {
|
|
383
|
+
payload: "qos 2 message",
|
|
384
|
+
retain: false,
|
|
385
|
+
id: 202,
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
await disconnect(publisher);
|
|
389
|
+
|
|
390
|
+
const subscriberReconnect = addMockClient(mqttServer);
|
|
391
|
+
|
|
392
|
+
await connect(subscriberReconnect, { clean: false, clientId });
|
|
393
|
+
|
|
394
|
+
const messages = await receiveMessages(subscriberReconnect);
|
|
395
|
+
|
|
396
|
+
const receivedTopics = messages
|
|
397
|
+
.filter((m): m is PublishPacket => m.type === PacketType.publish)
|
|
398
|
+
.map((m) => m.topic)
|
|
399
|
+
.sort();
|
|
400
|
+
|
|
401
|
+
assert.deepStrictEqual(
|
|
402
|
+
receivedTopics,
|
|
403
|
+
[topic1, topic3].sort(),
|
|
404
|
+
"Should receive both uncompleted QoS 1 and QoS 2 messages upon reconnect",
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
await disconnect(subscriberReconnect);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// ============================================================================
|
|
411
|
+
// MQTT v5 Specific Tests
|
|
412
|
+
// ============================================================================
|
|
413
|
+
|
|
414
|
+
test("SUBSCRIBE v5 returns SUBACK with reasonCodes and handles subscriptionIdentifier", async () => {
|
|
415
|
+
const { mqttConn } = startMockServer();
|
|
416
|
+
|
|
417
|
+
await connect5(mqttConn);
|
|
418
|
+
|
|
419
|
+
const subAck = await subscribe5(mqttConn, [{
|
|
420
|
+
topicFilter: "v5/test/topic",
|
|
421
|
+
qos: 1,
|
|
422
|
+
noLocal: true,
|
|
423
|
+
retainAsPublished: true,
|
|
424
|
+
retainHandling: 0,
|
|
425
|
+
}], { id: 100, subscriptionIdentifier: 42 });
|
|
426
|
+
|
|
427
|
+
assert.deepStrictEqual(subAck.id, 100);
|
|
428
|
+
assert.deepStrictEqual(
|
|
429
|
+
subAck.reasonCodes,
|
|
430
|
+
[ReasonCode.grantedQos1],
|
|
431
|
+
"Expected reasonCodes matching requested QoS 1 for MQTT v5",
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
await disconnect5(mqttConn);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
test("SUBSCRIBE v5 to unauthorized topic returns ReasonCode.notAuthorized (0x87)", async () => {
|
|
438
|
+
const { mqttConn } = startMockServer();
|
|
439
|
+
|
|
440
|
+
await connect5(mqttConn);
|
|
441
|
+
|
|
442
|
+
const subAck = await subscribe5(mqttConn, [{
|
|
443
|
+
topicFilter: "topic/unauthorized",
|
|
444
|
+
qos: 0,
|
|
445
|
+
}], {
|
|
446
|
+
id: 101,
|
|
447
|
+
checkAcks: false,
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
assert.deepStrictEqual(subAck.type, PacketType.suback, "Expected SUBACK");
|
|
451
|
+
assert.deepStrictEqual(subAck.id, 101);
|
|
452
|
+
assert.deepStrictEqual(
|
|
453
|
+
subAck.reasonCodes,
|
|
454
|
+
[ReasonCode.notAuthorized],
|
|
455
|
+
"Return code should be 0x87 (ReasonCode.notAuthorized) for MQTT v5",
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
await disconnect5(mqttConn);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
test("SUBSCRIBE v5 handles mixed authorized and unauthorized topics", async () => {
|
|
462
|
+
const { mqttConn } = startMockServer();
|
|
463
|
+
|
|
464
|
+
await connect5(mqttConn);
|
|
465
|
+
|
|
466
|
+
const subAck = await subscribe5(mqttConn, [
|
|
467
|
+
{ topicFilter: "topic/authorized", qos: 1 },
|
|
468
|
+
{ topicFilter: "topic/unauthorized", qos: 2 },
|
|
469
|
+
], { id: 102, checkAcks: false });
|
|
470
|
+
|
|
471
|
+
assert.deepStrictEqual(subAck.type, PacketType.suback, "Expected SUBACK");
|
|
472
|
+
|
|
473
|
+
assert.deepStrictEqual(subAck.id, 102);
|
|
474
|
+
assert.deepStrictEqual(
|
|
475
|
+
subAck.reasonCodes,
|
|
476
|
+
[ReasonCode.grantedQos1, ReasonCode.notAuthorized],
|
|
477
|
+
"Should return success code for authorized topic and 0x87 for unauthorized topic in exact order",
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
await disconnect5(mqttConn);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
// ============================================================================
|
|
484
|
+
// Edge Cases & Error Handling
|
|
485
|
+
// ============================================================================
|
|
486
|
+
|
|
487
|
+
test("SUBSCRIBE does not trigger retained messages when all subscriptions fail authorization", async () => {
|
|
488
|
+
const { mqttConn: publisher, mqttServer } = startMockServer();
|
|
489
|
+
|
|
490
|
+
// Set up a retained message first
|
|
491
|
+
await connect(publisher);
|
|
492
|
+
await publish(publisher, "topic/unauthorized", 0, {
|
|
493
|
+
payload: "secret-retained",
|
|
494
|
+
retain: true,
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
await disconnect(publisher);
|
|
498
|
+
|
|
499
|
+
const subscriber = addMockClient(mqttServer);
|
|
500
|
+
await connect(subscriber);
|
|
501
|
+
|
|
502
|
+
const subAck = await subscribe(subscriber, [{
|
|
503
|
+
topicFilter: "topic/unauthorized",
|
|
504
|
+
qos: 0,
|
|
505
|
+
}], {
|
|
506
|
+
id: 104,
|
|
507
|
+
checkAcks: false,
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
// Expect SUBACK with 128 (0x80)
|
|
511
|
+
assert.deepStrictEqual(subAck.type, PacketType.suback);
|
|
512
|
+
assert.deepStrictEqual(subAck.returnCodes, [128]);
|
|
513
|
+
|
|
514
|
+
// Ping to verify no retained message was forwarded
|
|
515
|
+
await ping(subscriber);
|
|
516
|
+
await disconnect(subscriber);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
test("SUBSCRIBE handles error thrown inside isAuthorizedToSubscribe gracefully", async () => {
|
|
520
|
+
const { mqttConn, mqttServer } = startMockServer();
|
|
521
|
+
|
|
522
|
+
// Force authorization handler to throw an error
|
|
523
|
+
mqttServer.handlers.isAuthorizedToSubscribe = () => {
|
|
524
|
+
throw new Error("Authorization handler internal failure");
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
await connect(mqttConn);
|
|
528
|
+
const subAck = await subscribe(mqttConn, [{
|
|
529
|
+
topicFilter: "sensors/temperature",
|
|
530
|
+
qos: 0,
|
|
531
|
+
}], {
|
|
532
|
+
id: 105,
|
|
533
|
+
checkAcks: false,
|
|
534
|
+
});
|
|
535
|
+
// Expect SUBACK with 128 (0x80)
|
|
536
|
+
assert.deepStrictEqual(subAck.type, PacketType.suback);
|
|
537
|
+
assert.deepStrictEqual(subAck.returnCodes, [128]);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
// ============================================================================
|
|
541
|
+
// Wildcard Restriction Tests
|
|
542
|
+
// ============================================================================
|
|
543
|
+
|
|
544
|
+
test("SUBSCRIBE v4 closes connection when wildcardSubscriptionAvailable is false", async () => {
|
|
545
|
+
const { mqttConn } = startMockServer({
|
|
546
|
+
configuration: { context: { wildcardSubscriptionAvailable: false } },
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
await connect(mqttConn);
|
|
550
|
+
|
|
551
|
+
await subscribe(mqttConn, [{ topicFilter: "sensors/+", qos: 0 }], {
|
|
552
|
+
id: 1,
|
|
553
|
+
checkAcks: false,
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
await mqttConn.next();
|
|
557
|
+
assert.strictEqual(
|
|
558
|
+
mqttConn.isClosed,
|
|
559
|
+
true,
|
|
560
|
+
"Expected connection to be closed in v4",
|
|
561
|
+
);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
test("SUBSCRIBE v5 returns wildcardSubscriptionsNotSupported when wildcardSubscriptionAvailable is false", async () => {
|
|
565
|
+
const { mqttConn } = startMockServer({
|
|
566
|
+
configuration: { context: { wildcardSubscriptionAvailable: false } },
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
await connect5(mqttConn);
|
|
570
|
+
|
|
571
|
+
const subAck = await subscribe5(mqttConn, [{
|
|
572
|
+
topicFilter: "sensors/+",
|
|
573
|
+
qos: 0,
|
|
574
|
+
}], {
|
|
575
|
+
id: 2,
|
|
576
|
+
checkAcks: false,
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
assert.strictEqual(subAck.type, PacketType.suback);
|
|
580
|
+
assert.strictEqual(subAck.id, 2);
|
|
581
|
+
assert.deepStrictEqual(
|
|
582
|
+
subAck.reasonCodes,
|
|
583
|
+
[ReasonCode.wildcardSubscriptionsNotSupported],
|
|
584
|
+
"Expected wildcardSubscriptionsNotSupported (0xA2) in v5",
|
|
585
|
+
);
|
|
586
|
+
|
|
587
|
+
await disconnect5(mqttConn);
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
// ============================================================================
|
|
591
|
+
// Topic Filter Validation Tests
|
|
592
|
+
// ============================================================================
|
|
593
|
+
|
|
594
|
+
test("SUBSCRIBE v4 closes connection on invalid topic filter", async () => {
|
|
595
|
+
const { mqttConn } = startMockServer();
|
|
596
|
+
|
|
597
|
+
await connect(mqttConn);
|
|
598
|
+
|
|
599
|
+
// Invalid wildcard placement
|
|
600
|
+
await subscribe(mqttConn, [{ topicFilter: "invalid/#/topic", qos: 0 }], {
|
|
601
|
+
id: 3,
|
|
602
|
+
checkAcks: false,
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
await mqttConn.next();
|
|
606
|
+
assert.strictEqual(
|
|
607
|
+
mqttConn.isClosed,
|
|
608
|
+
true,
|
|
609
|
+
"Expected connection to be closed in v4",
|
|
610
|
+
);
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
test("SUBSCRIBE v5 returns topicFilterInvalid on invalid topic filter or empty filter", async () => {
|
|
614
|
+
const { mqttConn } = startMockServer();
|
|
615
|
+
|
|
616
|
+
await connect5(mqttConn);
|
|
617
|
+
|
|
618
|
+
const subAck = await subscribe5(mqttConn, [
|
|
619
|
+
{ topicFilter: "", qos: 0 },
|
|
620
|
+
{ topicFilter: "invalid/#/topic", qos: 1 },
|
|
621
|
+
], {
|
|
622
|
+
id: 4,
|
|
623
|
+
checkAcks: false,
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
assert.strictEqual(subAck.type, PacketType.suback);
|
|
627
|
+
assert.strictEqual(subAck.id, 4);
|
|
628
|
+
assert.deepStrictEqual(
|
|
629
|
+
subAck.reasonCodes,
|
|
630
|
+
[ReasonCode.topicFilterInvalid, ReasonCode.topicFilterInvalid],
|
|
631
|
+
"Expected topicFilterInvalid (0x8F) for invalid topic filters in v5",
|
|
632
|
+
);
|
|
633
|
+
|
|
634
|
+
await disconnect5(mqttConn);
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
test("SUBSCRIBE v5 returns topicFilterInvalid when topic levels exceed maxTopicLevels", async () => {
|
|
638
|
+
const { mqttConn } = startMockServer({
|
|
639
|
+
configuration: { context: { maxTopicLevels: 1 } },
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
await connect5(mqttConn);
|
|
643
|
+
|
|
644
|
+
const subAck = await subscribe5(mqttConn, [{
|
|
645
|
+
topicFilter: "level1/level2",
|
|
646
|
+
qos: 0,
|
|
647
|
+
}], {
|
|
648
|
+
id: 5,
|
|
649
|
+
checkAcks: false,
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
assert.strictEqual(subAck.type, PacketType.suback);
|
|
653
|
+
assert.deepStrictEqual(
|
|
654
|
+
subAck.reasonCodes,
|
|
655
|
+
[ReasonCode.topicFilterInvalid],
|
|
656
|
+
"Expected topicFilterInvalid when exceeding maxTopicLevels",
|
|
657
|
+
);
|
|
658
|
+
|
|
659
|
+
await disconnect5(mqttConn);
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
// ============================================================================
|
|
663
|
+
// MQTT v5 Retain Handling Tests
|
|
664
|
+
// ============================================================================
|
|
665
|
+
|
|
666
|
+
test("SUBSCRIBE v5 with retainHandling = 2 does not send retained message", async () => {
|
|
667
|
+
const { mqttConn: publisher, mqttServer } = startMockServer();
|
|
668
|
+
|
|
669
|
+
// Publish retained message
|
|
670
|
+
await connect(publisher);
|
|
671
|
+
await publish(publisher, "retained/handling2", 0, {
|
|
672
|
+
payload: "should-not-be-sent",
|
|
673
|
+
retain: true,
|
|
674
|
+
});
|
|
675
|
+
await disconnect(publisher);
|
|
676
|
+
|
|
677
|
+
const subscriber = addMockClient(mqttServer);
|
|
678
|
+
await connect5(subscriber);
|
|
679
|
+
|
|
680
|
+
// Subscribe with retainHandling = 2 (Do not send retained messages)
|
|
681
|
+
await subscribe5(subscriber, [{
|
|
682
|
+
topicFilter: "retained/handling2",
|
|
683
|
+
qos: 0,
|
|
684
|
+
retainHandling: 2,
|
|
685
|
+
}], { id: 10 });
|
|
686
|
+
|
|
687
|
+
// PING to verify no retained PUBLISH packet was delivered after SUBACK
|
|
688
|
+
await ping(subscriber);
|
|
689
|
+
await disconnect5(subscriber);
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
test("SUBSCRIBE v5 with retainHandling = 1 sends retained message on new sub, skips on resubscribe", async () => {
|
|
693
|
+
const { mqttConn: publisher, mqttServer } = startMockServer();
|
|
694
|
+
|
|
695
|
+
await connect(publisher);
|
|
696
|
+
// Use QoS 1 so publish completes and persists before disconnecting
|
|
697
|
+
await publish(publisher, "retained/handling1", 1, {
|
|
698
|
+
id: 1,
|
|
699
|
+
payload: "first-delivery-only",
|
|
700
|
+
retain: true,
|
|
701
|
+
});
|
|
702
|
+
await disconnect(publisher);
|
|
703
|
+
|
|
704
|
+
const subscriber = addMockClient(mqttServer);
|
|
705
|
+
await connect5(subscriber);
|
|
706
|
+
|
|
707
|
+
// 1. Initial subscribe with retainHandling = 1 -> Should receive retained message
|
|
708
|
+
await subscribe5(subscriber, [{
|
|
709
|
+
topicFilter: "retained/handling1",
|
|
710
|
+
qos: 0,
|
|
711
|
+
retainHandling: 1,
|
|
712
|
+
}], { id: 20 });
|
|
713
|
+
|
|
714
|
+
const { value: publishPkt } = await subscriber.next();
|
|
715
|
+
assert.strictEqual(publishPkt.type, PacketType.publish);
|
|
716
|
+
assert.strictEqual(publishPkt.topic, "retained/handling1");
|
|
717
|
+
|
|
718
|
+
// 2. Re-subscribe to the exact same topic with retainHandling = 1 -> Should NOT receive retained message again
|
|
719
|
+
await subscribe5(subscriber, [{
|
|
720
|
+
topicFilter: "retained/handling1",
|
|
721
|
+
qos: 0,
|
|
722
|
+
retainHandling: 1,
|
|
723
|
+
}], { id: 21 });
|
|
724
|
+
|
|
725
|
+
await ping(subscriber);
|
|
726
|
+
await disconnect5(subscriber);
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
// ============================================================================
|
|
730
|
+
// Shared Subscriptions & Options Tests
|
|
731
|
+
// ============================================================================
|
|
732
|
+
|
|
733
|
+
test("SUBSCRIBE v5 returns sharedSubscriptionsNotSupported when shared subscriptions are disabled", async () => {
|
|
734
|
+
const { mqttConn } = startMockServer({
|
|
735
|
+
configuration: { context: { sharedSubscriptionAvailable: false } },
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
await connect5(mqttConn);
|
|
739
|
+
|
|
740
|
+
const subAck = await subscribe5(mqttConn, [{
|
|
741
|
+
topicFilter: "$share/group/sensors/temperature",
|
|
742
|
+
qos: 0,
|
|
743
|
+
}], {
|
|
744
|
+
id: 200,
|
|
745
|
+
checkAcks: false,
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
assert.strictEqual(subAck.type, PacketType.suback);
|
|
749
|
+
assert.strictEqual(subAck.id, 200);
|
|
750
|
+
assert.deepStrictEqual(
|
|
751
|
+
subAck.reasonCodes,
|
|
752
|
+
[ReasonCode.sharedSubscriptionsNotSupported],
|
|
753
|
+
"Expected sharedSubscriptionsNotSupported (0x8E) in v5",
|
|
754
|
+
);
|
|
755
|
+
|
|
756
|
+
await disconnect5(mqttConn);
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
test("SUBSCRIBE v4 closes connection on shared subscription when shared subscriptions are disabled", async () => {
|
|
760
|
+
const { mqttConn } = startMockServer({
|
|
761
|
+
configuration: { context: { sharedSubscriptionAvailable: false } },
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
await connect(mqttConn);
|
|
765
|
+
|
|
766
|
+
await subscribe(mqttConn, [{
|
|
767
|
+
topicFilter: "$share/group/sensors/temperature",
|
|
768
|
+
qos: 0,
|
|
769
|
+
}], {
|
|
770
|
+
id: 201,
|
|
771
|
+
checkAcks: false,
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
await mqttConn.next();
|
|
775
|
+
assert.strictEqual(
|
|
776
|
+
mqttConn.isClosed,
|
|
777
|
+
true,
|
|
778
|
+
"Expected connection to be closed in v4",
|
|
779
|
+
);
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
test("SUBSCRIBE v5 returns topicFilterInvalid when noLocal is set on a shared subscription", async () => {
|
|
783
|
+
const { mqttConn } = startMockServer({
|
|
784
|
+
configuration: { context: { sharedSubscriptionAvailable: true } },
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
await connect5(mqttConn);
|
|
788
|
+
|
|
789
|
+
const subAck = await subscribe5(mqttConn, [{
|
|
790
|
+
topicFilter: "$share/group/sensors/temperature",
|
|
791
|
+
qos: 1,
|
|
792
|
+
noLocal: true,
|
|
793
|
+
}], {
|
|
794
|
+
id: 202,
|
|
795
|
+
checkAcks: false,
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
assert.strictEqual(subAck.type, PacketType.suback);
|
|
799
|
+
assert.strictEqual(subAck.id, 202);
|
|
800
|
+
assert.deepStrictEqual(
|
|
801
|
+
subAck.reasonCodes,
|
|
802
|
+
[ReasonCode.topicFilterInvalid],
|
|
803
|
+
"Expected topicFilterInvalid (0x8F) when noLocal is true on shared subscription",
|
|
804
|
+
);
|
|
805
|
+
|
|
806
|
+
await disconnect5(mqttConn);
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
test("SUBSCRIBE v4 closes connection when a shared subscription is used", async () => {
|
|
810
|
+
const { mqttConn } = startMockServer({
|
|
811
|
+
configuration: { context: { sharedSubscriptionAvailable: true } },
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
await connect(mqttConn);
|
|
815
|
+
|
|
816
|
+
await subscribe(mqttConn, [{
|
|
817
|
+
topicFilter: "$share/group/sensors/temperature",
|
|
818
|
+
qos: 1,
|
|
819
|
+
}], {
|
|
820
|
+
id: 203,
|
|
821
|
+
checkAcks: false,
|
|
822
|
+
});
|
|
823
|
+
|
|
824
|
+
await mqttConn.next();
|
|
825
|
+
assert.strictEqual(
|
|
826
|
+
mqttConn.isClosed,
|
|
827
|
+
true,
|
|
828
|
+
"Expected connection to be closed in v4",
|
|
829
|
+
);
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
// ============================================================================
|
|
833
|
+
// Additional MQTT v4 Validation Edge Cases
|
|
834
|
+
// ============================================================================
|
|
835
|
+
|
|
836
|
+
test("SUBSCRIBE v4 closes connection when topicFilter is empty", async () => {
|
|
837
|
+
const { mqttConn } = startMockServer();
|
|
838
|
+
|
|
839
|
+
await connect(mqttConn);
|
|
840
|
+
|
|
841
|
+
await subscribe(mqttConn, [{ topicFilter: "", qos: 0 }], {
|
|
842
|
+
id: 204,
|
|
843
|
+
checkAcks: false,
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
await mqttConn.next();
|
|
847
|
+
assert.strictEqual(
|
|
848
|
+
mqttConn.isClosed,
|
|
849
|
+
true,
|
|
850
|
+
"Expected connection to be closed in v4 for empty topic filter",
|
|
851
|
+
);
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
test("SUBSCRIBE v4 closes connection when topic levels exceed maxTopicLevels", async () => {
|
|
855
|
+
const { mqttConn } = startMockServer({
|
|
856
|
+
configuration: { context: { maxTopicLevels: 1 } },
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
await connect(mqttConn);
|
|
860
|
+
|
|
861
|
+
await subscribe(mqttConn, [{ topicFilter: "level1/level2", qos: 0 }], {
|
|
862
|
+
id: 205,
|
|
863
|
+
checkAcks: false,
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
await mqttConn.next();
|
|
867
|
+
assert.strictEqual(
|
|
868
|
+
mqttConn.isClosed,
|
|
869
|
+
true,
|
|
870
|
+
"Expected connection to be closed in v4 when exceeding maxTopicLevels",
|
|
871
|
+
);
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
// ============================================================================
|
|
875
|
+
// Subscription Flags Behavior Tests (noLocal & retainAsPublished)
|
|
876
|
+
// ============================================================================
|
|
877
|
+
|
|
878
|
+
test("SUBSCRIBE v5 with noLocal = true does not deliver own published messages", async () => {
|
|
879
|
+
const { mqttConn: client, mqttServer } = startMockServer();
|
|
880
|
+
mqttServer.handlers.isAuthenticated = isAuthenticatedBroker;
|
|
881
|
+
|
|
882
|
+
await connect5(client, { clientId: "nolocal-client" });
|
|
883
|
+
|
|
884
|
+
await subscribe5(client, [{
|
|
885
|
+
topicFilter: "nolocal/test",
|
|
886
|
+
qos: 1,
|
|
887
|
+
noLocal: true,
|
|
888
|
+
}], { id: 206 });
|
|
889
|
+
|
|
890
|
+
// Publish message on the same connection
|
|
891
|
+
await publish5(client, "nolocal/test", 0, {
|
|
892
|
+
payload: "self-published-msg",
|
|
893
|
+
id: 50,
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
// Check with PING that no PUBLISH packet is delivered back
|
|
897
|
+
await ping(client);
|
|
898
|
+
|
|
899
|
+
await disconnect5(client);
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
test("SUBSCRIBE v5 with retainAsPublished = false strips retain flag on delivery", async () => {
|
|
903
|
+
const { mqttConn: publisher, mqttServer } = startMockServer();
|
|
904
|
+
|
|
905
|
+
await connect5(publisher);
|
|
906
|
+
|
|
907
|
+
const subscriber = addMockClient(mqttServer);
|
|
908
|
+
await connect5(subscriber);
|
|
909
|
+
|
|
910
|
+
// Subscribe with retainAsPublished: false
|
|
911
|
+
await subscribe5(subscriber, [{
|
|
912
|
+
topicFilter: "rap/test",
|
|
913
|
+
qos: 1,
|
|
914
|
+
retainAsPublished: false,
|
|
915
|
+
}], { id: 207 });
|
|
916
|
+
|
|
917
|
+
// Publish with retain: true
|
|
918
|
+
await publish5(publisher, "rap/test", 1, {
|
|
919
|
+
payload: "retained-content",
|
|
920
|
+
retain: true,
|
|
921
|
+
id: 60,
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
const { value: publishPkt } = await subscriber.next();
|
|
925
|
+
assert.strictEqual(publishPkt.type, PacketType.publish);
|
|
926
|
+
assert.strictEqual(
|
|
927
|
+
publishPkt.retain,
|
|
928
|
+
false,
|
|
929
|
+
"Expected retain flag to be cleared on delivery when retainAsPublished is false",
|
|
930
|
+
);
|
|
931
|
+
|
|
932
|
+
await disconnect5(publisher);
|
|
933
|
+
await disconnect5(subscriber);
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
// ============================================================================
|
|
937
|
+
// Combined Validation and Authorization Results Test
|
|
938
|
+
// ============================================================================
|
|
939
|
+
|
|
940
|
+
test("SUBSCRIBE v5 handles mixed validation errors and authorization failures in strict order", async () => {
|
|
941
|
+
const { mqttConn } = startMockServer({
|
|
942
|
+
configuration: { context: { sharedSubscriptionAvailable: false } },
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
await connect5(mqttConn);
|
|
946
|
+
|
|
947
|
+
const subAck = await subscribe5(mqttConn, [
|
|
948
|
+
{ topicFilter: "topic/authorized", qos: 1 },
|
|
949
|
+
{ topicFilter: "invalid/#/topic", qos: 0 },
|
|
950
|
+
{ topicFilter: "topic/unauthorized", qos: 2 },
|
|
951
|
+
{ topicFilter: "$share/group/test", qos: 1 },
|
|
952
|
+
], {
|
|
953
|
+
id: 208,
|
|
954
|
+
checkAcks: false,
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
assert.strictEqual(subAck.type, PacketType.suback);
|
|
958
|
+
assert.strictEqual(subAck.id, 208);
|
|
959
|
+
assert.deepStrictEqual(
|
|
960
|
+
subAck.reasonCodes,
|
|
961
|
+
[
|
|
962
|
+
ReasonCode.grantedQos1,
|
|
963
|
+
ReasonCode.topicFilterInvalid,
|
|
964
|
+
ReasonCode.notAuthorized,
|
|
965
|
+
ReasonCode.sharedSubscriptionsNotSupported,
|
|
966
|
+
],
|
|
967
|
+
"Expected exact sequence of reason codes matching subscription packet order",
|
|
968
|
+
);
|
|
969
|
+
|
|
970
|
+
await disconnect5(mqttConn);
|
|
971
|
+
});
|