@seriousme/opifex 1.6.1 → 1.6.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/.github/dependabot.yml +7 -0
- package/.github/workflows/closeStale.yml +1 -1
- package/.github/workflows/codeql-analysis.yml +4 -4
- package/CHANGELOG.md +12 -0
- package/README.md +6 -7
- package/bin/conformanceServer.ts +67 -16
- package/bin/demoServer.ts +69 -14
- package/bin/mqtt.ts +30 -0
- package/client/ConnectionState.ts +12 -0
- package/client/client.ts +56 -2
- package/client/context.ts +5 -0
- package/client/handlers/handleConnack.ts +6 -0
- package/client/handlers/handlePacket.ts +7 -0
- package/client/handlers/handlePuback.ts +8 -2
- package/client/handlers/handlePubcomp.ts +10 -4
- package/client/handlers/handlePublish.ts +6 -2
- package/client/handlers/handlePubrec.ts +13 -6
- package/client/handlers/handlePubrel.ts +16 -6
- package/client/handlers/handleSuback.ts +9 -6
- package/client/handlers/handleUnsuback.ts +10 -3
- package/client/store/memoryStore.ts +25 -0
- package/client/store/store.ts +39 -0
- package/deno/tcpClient.ts +19 -2
- package/deno/tcpServer.test.ts +137 -2
- package/deno/tcpServer.ts +9 -2
- package/deno.json +2 -1
- package/dist/bin/conformanceServer.js +61 -15
- package/dist/bin/demoServer.js +63 -13
- package/dist/bin/mqtt.js +29 -0
- package/dist/client/ConnectionState.js +9 -0
- package/dist/client/client.js +56 -1
- package/dist/client/context.js +4 -0
- package/dist/client/handlers/handleConnack.js +6 -0
- package/dist/client/handlers/handlePacket.js +7 -0
- package/dist/client/handlers/handlePuback.js +8 -1
- package/dist/client/handlers/handlePubcomp.js +10 -3
- package/dist/client/handlers/handlePublish.js +6 -1
- package/dist/client/handlers/handlePubrec.js +13 -4
- package/dist/client/handlers/handlePubrel.js +16 -4
- package/dist/client/handlers/handleSuback.js +9 -4
- package/dist/client/handlers/handleUnsuback.js +10 -2
- package/dist/client/store/memoryStore.js +22 -0
- package/dist/client/store/store.js +3 -0
- package/dist/mqttPacket/AuthenticationResult.js +3 -0
- package/dist/mqttPacket/mod.js +5 -0
- package/dist/node/tcpClient.js +19 -2
- package/dist/node/tcpServer.js +12 -2
- package/dist/node/wrapNodeSocket.js +9 -0
- package/dist/persistence/memory/memoryPersistence.js +1 -0
- package/dist/server/handlers/handleConnect.js +39 -5
- package/dist/server/handlers/handleDisconnect.js +5 -0
- package/dist/server/handlers/handlePacket.js +7 -0
- package/dist/server/handlers/handlePingreq.js +5 -0
- package/dist/server/handlers/handlePuback.js +9 -1
- package/dist/server/handlers/handlePubcomp.js +8 -2
- package/dist/server/handlers/handlePublish.js +13 -0
- package/dist/server/handlers/handlePubrec.js +11 -3
- package/dist/server/handlers/handlePubrel.js +12 -3
- package/dist/server/handlers/handleSubscribe.js +27 -4
- package/dist/server/handlers/handleUnsubscribe.js +6 -0
- package/dist/utils/args.js +14 -0
- package/dist/utils/assert.js +1 -0
- package/dist/utils/asyncQueue.js +13 -0
- package/dist/utils/deferred.js +3 -0
- package/dist/utils/logger.js +29 -0
- package/dist/utils/mod.js +4 -0
- package/dist/utils/nextTick.js +15 -0
- package/dist/utils/platform.js +3 -0
- package/mqttPacket/AuthenticationResult.ts +3 -1
- package/mqttPacket/connack.ts +4 -0
- package/mqttPacket/connect.ts +3 -0
- package/mqttPacket/disconnect.ts +4 -0
- package/mqttPacket/mod.ts +9 -0
- package/mqttPacket/pingreq.ts +3 -0
- package/mqttPacket/pingres.ts +4 -0
- package/mqttPacket/puback.ts +3 -0
- package/mqttPacket/pubcomp.ts +4 -0
- package/mqttPacket/publish.ts +4 -0
- package/mqttPacket/pubrec.ts +4 -0
- package/mqttPacket/pubrel.ts +4 -0
- package/mqttPacket/suback.ts +4 -0
- package/mqttPacket/subscribe.ts +6 -0
- package/mqttPacket/types.ts +32 -1
- package/mqttPacket/unsuback.ts +4 -0
- package/mqttPacket/unsubscribe.ts +3 -0
- package/node/tcpClient.ts +20 -2
- package/node/tcpServer.test.ts +61 -6
- package/node/tcpServer.ts +17 -4
- package/node/wrapNodeSocket.ts +13 -2
- package/package.json +2 -2
- package/persistence/memory/memoryPersistence.test.ts +2 -1
- package/persistence/memory/memoryPersistence.ts +1 -0
- package/persistence/store.ts +1 -0
- package/server/handlers/handleConnect.ts +49 -6
- package/server/handlers/handleDisconnect.ts +5 -0
- package/server/handlers/handlePacket.ts +7 -0
- package/server/handlers/handlePingreq.ts +6 -0
- package/server/handlers/handlePuback.ts +9 -2
- package/server/handlers/handlePubcomp.ts +8 -3
- package/server/handlers/handlePublish.ts +13 -0
- package/server/handlers/handlePubrec.ts +11 -3
- package/server/handlers/handlePubrel.ts +12 -4
- package/server/handlers/handleSubscribe.ts +28 -5
- package/server/handlers/handleUnsubscribe.ts +6 -0
- package/utils/args.ts +17 -0
- package/utils/assert.ts +1 -0
- package/utils/asyncQueue.ts +13 -1
- package/utils/deferred.ts +4 -0
- package/utils/logger.ts +29 -0
- package/utils/mod.ts +4 -0
- package/utils/nextTick.ts +15 -0
- package/utils/platform.ts +4 -0
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { PacketType } from "../deps.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Handles PUBREL (QoS 2 publish release) packets
|
|
4
|
+
*
|
|
5
|
+
* @param ctx - The connection context
|
|
6
|
+
* @param packet - The PUBREL packet received from the client
|
|
7
|
+
* @returns Promise that resolves when handling is complete
|
|
8
|
+
* @description
|
|
9
|
+
* For QoS 2 message delivery:
|
|
10
|
+
* 1. Initiates onward delivery of the Application Message
|
|
11
|
+
* 2. Discards the stored message
|
|
12
|
+
* 3. Sends PUBCOMP packet with the Packet Identifier
|
|
13
|
+
*/
|
|
5
14
|
export async function handlePubrel(ctx, packet) {
|
|
6
15
|
const id = packet.id;
|
|
7
16
|
if (ctx.store?.pendingIncoming.has(id)) {
|
|
@@ -1,14 +1,35 @@
|
|
|
1
1
|
import { PacketType, } from "../deps.js";
|
|
2
|
+
/**
|
|
3
|
+
* @constant {number} SubscriptionFailure
|
|
4
|
+
* @description Code indicating a failed subscription attempt
|
|
5
|
+
*/
|
|
2
6
|
const SubscriptionFailure = 0x80;
|
|
7
|
+
/**
|
|
8
|
+
* Checks if a client is authorized to subscribe to a topic
|
|
9
|
+
* @param ctx - The connection context
|
|
10
|
+
* @param topicFilter - The topic filter to check authorization for
|
|
11
|
+
* @returns True if authorized, false otherwise
|
|
12
|
+
*/
|
|
3
13
|
function authorizedToSubscribe(ctx, topicFilter) {
|
|
4
14
|
if (ctx.handlers.isAuthorizedToSubscribe) {
|
|
5
15
|
return ctx.handlers.isAuthorizedToSubscribe(ctx, topicFilter);
|
|
6
16
|
}
|
|
7
17
|
return true;
|
|
8
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* @function handleSubscribe
|
|
21
|
+
* @description Processes an MQTT SUBSCRIBE packet
|
|
22
|
+
* @param {Context} ctx - The connection context
|
|
23
|
+
* @param {SubscribePacket} packet - The SUBSCRIBE packet received from the client
|
|
24
|
+
* @returns {Promise<void>}
|
|
25
|
+
* @throws {Error} If subscription processing fails
|
|
26
|
+
* @remarks The order of return codes in the SUBACK Packet MUST match the order of Topic Filters in the SUBSCRIBE Packet [MQTT-3.9.3-1]
|
|
27
|
+
*/
|
|
9
28
|
export async function handleSubscribe(ctx, packet) {
|
|
10
|
-
|
|
11
|
-
|
|
29
|
+
/*
|
|
30
|
+
* The order of return codes in the SUBACK Packet MUST match the order of
|
|
31
|
+
* Topic Filters in the SUBSCRIBE Packet [MQTT-3.9.3-1].
|
|
32
|
+
*/
|
|
12
33
|
const validSubscriptions = [];
|
|
13
34
|
const returnCodes = packet.subscriptions.map((sub) => {
|
|
14
35
|
if (ctx.store) {
|
|
@@ -19,14 +40,16 @@ export async function handleSubscribe(ctx, packet) {
|
|
|
19
40
|
validSubscriptions.push(sub);
|
|
20
41
|
return sub.qos;
|
|
21
42
|
}
|
|
22
|
-
return SubscriptionFailure;
|
|
43
|
+
return SubscriptionFailure;
|
|
23
44
|
});
|
|
24
45
|
await ctx.send({
|
|
25
46
|
type: PacketType.suback,
|
|
26
47
|
id: packet.id,
|
|
27
48
|
returnCodes: returnCodes,
|
|
28
49
|
});
|
|
29
|
-
|
|
50
|
+
/*
|
|
51
|
+
* send any retained messages that match these subscriptions
|
|
52
|
+
*/
|
|
30
53
|
if (ctx.store) {
|
|
31
54
|
ctx.persistence.handleRetained(ctx.store.clientId);
|
|
32
55
|
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { PacketType } from "../deps.js";
|
|
2
|
+
/**
|
|
3
|
+
* Handles MQTT unsubscribe packets by removing subscriptions and sending acknowledgement
|
|
4
|
+
* @param ctx - The connection context containing client information and methods
|
|
5
|
+
* @param packet - The MQTT unsubscribe packet containing topics to unsubscribe from
|
|
6
|
+
* @returns Promise that resolves when unsubscribe is complete and acknowledged
|
|
7
|
+
*/
|
|
2
8
|
export async function handleUnsubscribe(ctx, packet) {
|
|
3
9
|
for (const topic of packet.topicFilters) {
|
|
4
10
|
if (ctx.store) {
|
package/dist/utils/args.js
CHANGED
|
@@ -32,6 +32,20 @@ function parseKeyValue(arg, alias) {
|
|
|
32
32
|
}
|
|
33
33
|
return ["_", arg];
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* a parser for command line arguments
|
|
37
|
+
* @param args the arguments to parse
|
|
38
|
+
* @param opts the options for the parser
|
|
39
|
+
* @returns the parsed arguments
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* const args = parseArgs(args, {
|
|
43
|
+
* alias: { h: "help" },
|
|
44
|
+
* boolean: ["help"],
|
|
45
|
+
* string: ["port"],
|
|
46
|
+
* default: { port: 3000 },
|
|
47
|
+
* });
|
|
48
|
+
*/
|
|
35
49
|
export function parseArgs(args, opts = {}) {
|
|
36
50
|
const { alias = {}, boolean = [], string = [], default: defaults = {} } = opts;
|
|
37
51
|
const result = { ...defaults };
|
package/dist/utils/assert.js
CHANGED
package/dist/utils/asyncQueue.js
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import { nextTick } from "./nextTick.js";
|
|
2
|
+
/**
|
|
3
|
+
* An Async Queue is a queue that can be used to push items to it and then wait
|
|
4
|
+
* for them to be consumed.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* const queue = new AsyncQueue<string>();
|
|
9
|
+
* queue.push("hello");
|
|
10
|
+
* for await (const item of queue) {
|
|
11
|
+
* console.log(item); // "hello"
|
|
12
|
+
* }
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
2
15
|
export class AsyncQueue {
|
|
3
16
|
queue = [];
|
|
4
17
|
maxQueueLength = Infinity;
|
package/dist/utils/deferred.js
CHANGED
package/dist/utils/logger.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The available log levels
|
|
3
|
+
*/
|
|
1
4
|
export const LogLevel = {
|
|
2
5
|
error: 0,
|
|
3
6
|
warn: 1,
|
|
@@ -5,6 +8,19 @@ export const LogLevel = {
|
|
|
5
8
|
verbose: 3,
|
|
6
9
|
debug: 4,
|
|
7
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
* Simple logger class
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const logger = new Logger();
|
|
16
|
+
* logger.level(LogLevel.debug);
|
|
17
|
+
* logger.debug("debug message");
|
|
18
|
+
* logger.verbose("verbose message");
|
|
19
|
+
* logger.info("info message");
|
|
20
|
+
* logger.warn("warn message");
|
|
21
|
+
* logger.error("error message");
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
8
24
|
export class Logger {
|
|
9
25
|
defaultError = console.error;
|
|
10
26
|
defaultWarn = console.warn;
|
|
@@ -26,4 +42,17 @@ export class Logger {
|
|
|
26
42
|
this.debug = logLevel > 3 ? this.defaultDebug : this.noop;
|
|
27
43
|
}
|
|
28
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* logger provides a singleton logger instance
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* import { logger } from "./utils/logger.ts";
|
|
50
|
+
* logger.level(LogLevel.debug);
|
|
51
|
+
* logger.debug("debug message");
|
|
52
|
+
* logger.verbose("verbose message");
|
|
53
|
+
* logger.info("info message");
|
|
54
|
+
* logger.warn("warn message");
|
|
55
|
+
* logger.error("error message");
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
29
58
|
export const logger = new Logger();
|
package/dist/utils/mod.js
CHANGED
package/dist/utils/nextTick.js
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a promise that resolves on the next tick of the Node.js event loop.
|
|
3
|
+
* This is useful for deferring execution of code until after the current call stack is cleared.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* async function example() {
|
|
8
|
+
* console.log('Before nextTick');
|
|
9
|
+
* await nextTick();
|
|
10
|
+
* console.log('After nextTick');
|
|
11
|
+
* }
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* @returns {Promise<void>} A promise that resolves on the next tick of the event loop
|
|
15
|
+
*/
|
|
1
16
|
export function nextTick() {
|
|
2
17
|
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
3
18
|
}
|
package/dist/utils/platform.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Possible MQTT authentication results
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
4
|
export const AuthenticationResult = {
|
|
6
5
|
ok: 0,
|
|
7
6
|
unacceptableProtocol: 1,
|
|
@@ -11,6 +10,9 @@ export const AuthenticationResult = {
|
|
|
11
10
|
notAuthorized: 5,
|
|
12
11
|
} as const;
|
|
13
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Reverse lookup for AuthenticationResult
|
|
15
|
+
*/
|
|
14
16
|
export const AuthenticationResultByNumber: Record<number, string> = Object
|
|
15
17
|
.fromEntries(
|
|
16
18
|
Object.entries(AuthenticationResult).map(([k, v]) => [v, k]),
|
package/mqttPacket/connack.ts
CHANGED
|
@@ -4,6 +4,10 @@ import { BitMask } from "./BitMask.ts";
|
|
|
4
4
|
import { booleanFlag, Decoder, DecoderError } from "./decoder.ts";
|
|
5
5
|
import { AuthenticationResultByNumber } from "./AuthenticationResult.ts";
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* ConnackPacket is sent from the server to the client in response to a connect packet.
|
|
9
|
+
* It indicates that the connect is accepted.
|
|
10
|
+
*/
|
|
7
11
|
export type ConnackPacket = {
|
|
8
12
|
type: TPacketType;
|
|
9
13
|
sessionPresent: boolean;
|
package/mqttPacket/connect.ts
CHANGED
package/mqttPacket/disconnect.ts
CHANGED
|
@@ -2,6 +2,10 @@ import { PacketType } from "./PacketType.ts";
|
|
|
2
2
|
import { isEmptyBuf } from "./decoder.ts";
|
|
3
3
|
import type { TPacketType } from "./types.ts";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* DisconnectPacket is the final control packet sent from the client to the server.
|
|
7
|
+
* It indicates that the client is disconnecting cleanly.
|
|
8
|
+
*/
|
|
5
9
|
export type DisconnectPacket = {
|
|
6
10
|
type: TPacketType;
|
|
7
11
|
};
|
package/mqttPacket/mod.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This module provides the encoding and decoding of MQTT packets
|
|
3
|
+
* it is used by the server and client modules
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import type {
|
|
2
8
|
ClientId,
|
|
3
9
|
Dup,
|
|
@@ -33,6 +39,9 @@ import { pingres, type PingresPacket } from "./pingres.ts";
|
|
|
33
39
|
import { disconnect, type DisconnectPacket } from "./disconnect.ts";
|
|
34
40
|
import { DecoderError } from "./decoder.ts";
|
|
35
41
|
|
|
42
|
+
/**
|
|
43
|
+
* this can be any possible MQTT packet
|
|
44
|
+
*/
|
|
36
45
|
export type AnyPacket =
|
|
37
46
|
| ConnectPacket
|
|
38
47
|
| ConnackPacket
|
package/mqttPacket/pingreq.ts
CHANGED
|
@@ -2,6 +2,9 @@ import { PacketType } from "./PacketType.ts";
|
|
|
2
2
|
import { hasEmptyFlags, isEmptyBuf } from "./decoder.ts";
|
|
3
3
|
import type { TPacketType } from "./types.ts";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* PingreqPacket is a packet that is sent to the server to keep the connection alive
|
|
7
|
+
*/
|
|
5
8
|
export type PingreqPacket = {
|
|
6
9
|
type: TPacketType;
|
|
7
10
|
};
|
package/mqttPacket/pingres.ts
CHANGED
|
@@ -2,6 +2,10 @@ import { PacketType } from "./PacketType.ts";
|
|
|
2
2
|
import { isEmptyBuf } from "./decoder.ts";
|
|
3
3
|
import type { TPacketType } from "./types.ts";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* PingresPacket is an empty packet that is sent by the server in response to a PingreqPacket.
|
|
7
|
+
* It is used to indicate that the client is still connected to the server.
|
|
8
|
+
*/
|
|
5
9
|
export type PingresPacket = {
|
|
6
10
|
type: TPacketType;
|
|
7
11
|
};
|
package/mqttPacket/puback.ts
CHANGED
|
@@ -3,6 +3,9 @@ import { PacketType } from "./PacketType.ts";
|
|
|
3
3
|
import { Decoder } from "./decoder.ts";
|
|
4
4
|
import { Encoder } from "./encoder.ts";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* PubackPacket is sent to indicate publish complete (QoS 1)
|
|
8
|
+
*/
|
|
6
9
|
export type PubackPacket = {
|
|
7
10
|
type: TPacketType;
|
|
8
11
|
id: PacketId;
|
package/mqttPacket/pubcomp.ts
CHANGED
|
@@ -3,6 +3,10 @@ import { PacketType } from "./PacketType.ts";
|
|
|
3
3
|
import { Decoder } from "./decoder.ts";
|
|
4
4
|
import { Encoder } from "./encoder.ts";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* PubcompPacket is sent to indicate publish complete
|
|
8
|
+
* It is the fourth packet of the QoS 2 protocol exchange.
|
|
9
|
+
*/
|
|
6
10
|
export type PubcompPacket = {
|
|
7
11
|
type: TPacketType;
|
|
8
12
|
id: PacketId;
|
package/mqttPacket/publish.ts
CHANGED
|
@@ -4,6 +4,10 @@ import { BitMask } from "./BitMask.ts";
|
|
|
4
4
|
import { Encoder, EncoderError } from "./encoder.ts";
|
|
5
5
|
import { booleanFlag, Decoder, DecoderError } from "./decoder.ts";
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* PublishPacket is used to send data from client to server and
|
|
9
|
+
* from server to subscribers
|
|
10
|
+
*/
|
|
7
11
|
export type PublishPacket = {
|
|
8
12
|
type: TPacketType;
|
|
9
13
|
topic: Topic;
|
package/mqttPacket/pubrec.ts
CHANGED
|
@@ -3,6 +3,10 @@ import { PacketType } from "./PacketType.ts";
|
|
|
3
3
|
import { Decoder } from "./decoder.ts";
|
|
4
4
|
import { Encoder } from "./encoder.ts";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* PubrecPacket indicates publish received
|
|
8
|
+
* It is the second packet of the QoS 2 protocol exchange.
|
|
9
|
+
*/
|
|
6
10
|
export type PubrecPacket = {
|
|
7
11
|
type: TPacketType;
|
|
8
12
|
id: PacketId;
|
package/mqttPacket/pubrel.ts
CHANGED
|
@@ -3,6 +3,10 @@ import { PacketType } from "./PacketType.ts";
|
|
|
3
3
|
import { Decoder } from "./decoder.ts";
|
|
4
4
|
import { Encoder } from "./encoder.ts";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* PubrelPacket is the packet sent by the Server to the Client in response to a
|
|
8
|
+
* PubrecPacket. It is the third packet of the QoS 2 protocol exchange.
|
|
9
|
+
*/
|
|
6
10
|
export type PubrelPacket = {
|
|
7
11
|
type: TPacketType;
|
|
8
12
|
id: PacketId;
|
package/mqttPacket/suback.ts
CHANGED
|
@@ -3,6 +3,10 @@ import { PacketType } from "./PacketType.ts";
|
|
|
3
3
|
import { Encoder } from "./encoder.ts";
|
|
4
4
|
import { Decoder } from "./decoder.ts";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* SubackPacket is sent by the server to the client to confirm receipt and
|
|
8
|
+
* processing of a SubscribePacket.
|
|
9
|
+
*/
|
|
6
10
|
export type SubackPacket = {
|
|
7
11
|
type: TPacketType;
|
|
8
12
|
id: PacketId;
|
package/mqttPacket/subscribe.ts
CHANGED
|
@@ -4,12 +4,18 @@ import { BitMask } from "./BitMask.ts";
|
|
|
4
4
|
import { Encoder } from "./encoder.ts";
|
|
5
5
|
import { booleanFlag, Decoder, DecoderError } from "./decoder.ts";
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* SubscribePacket is sent from client to server to subscribe to topics
|
|
9
|
+
*/
|
|
7
10
|
export type SubscribePacket = {
|
|
8
11
|
type: TPacketType;
|
|
9
12
|
id: PacketId;
|
|
10
13
|
subscriptions: Subscription[];
|
|
11
14
|
};
|
|
12
15
|
|
|
16
|
+
/**
|
|
17
|
+
* The topic to subscribe to and the associated QoS
|
|
18
|
+
*/
|
|
13
19
|
export type Subscription = {
|
|
14
20
|
topicFilter: TopicFilter;
|
|
15
21
|
qos: QoS;
|
package/mqttPacket/types.ts
CHANGED
|
@@ -2,26 +2,57 @@ import type { BitMask } from "./BitMask.ts";
|
|
|
2
2
|
import type { PacketType } from "./PacketType.ts";
|
|
3
3
|
import type { AuthenticationResult } from "./AuthenticationResult.ts";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Type to limit bit mask to valid values
|
|
7
|
+
*/
|
|
5
8
|
export type TBitMask = typeof BitMask[keyof typeof BitMask];
|
|
9
|
+
/**
|
|
10
|
+
* Type to limit packet type to valid values
|
|
11
|
+
*/
|
|
6
12
|
export type TPacketType = typeof PacketType[keyof typeof PacketType];
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Type to limit authentication result to valid values
|
|
16
|
+
*/
|
|
7
17
|
export type TAuthenticationResult =
|
|
8
18
|
typeof AuthenticationResult[keyof typeof AuthenticationResult];
|
|
9
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Quality of Service level
|
|
22
|
+
*/
|
|
10
23
|
export type QoS = 0 | 1 | 2;
|
|
11
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Packet payload
|
|
27
|
+
*/
|
|
12
28
|
export type Payload = Uint8Array;
|
|
13
29
|
|
|
14
30
|
/**
|
|
15
|
-
* The MQTT topic to publish
|
|
31
|
+
* The MQTT topic to publish to
|
|
16
32
|
*/
|
|
17
33
|
export type Topic = string;
|
|
18
34
|
|
|
35
|
+
/**
|
|
36
|
+
* The MQTT topic to subscribe/unsubscribe to
|
|
37
|
+
*/
|
|
19
38
|
export type TopicFilter = string;
|
|
20
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Duplicate delivery flag
|
|
42
|
+
*/
|
|
21
43
|
export type Dup = boolean;
|
|
22
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Packet identifier, unique per client session
|
|
47
|
+
*/
|
|
23
48
|
export type PacketId = number;
|
|
24
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Return codes on to describe result of subscribe operation
|
|
52
|
+
*/
|
|
25
53
|
export type ReturnCodes = number[];
|
|
26
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Client identifier that uniquely identifies a client
|
|
57
|
+
*/
|
|
27
58
|
export type ClientId = string;
|
package/mqttPacket/unsuback.ts
CHANGED
|
@@ -3,6 +3,10 @@ import { PacketType } from "./PacketType.ts";
|
|
|
3
3
|
import { Decoder } from "./decoder.ts";
|
|
4
4
|
import { Encoder } from "./encoder.ts";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* UnsubackPacket is sent by the server to the client to confirm receipt
|
|
8
|
+
* of an UnsubscribePacket.
|
|
9
|
+
*/
|
|
6
10
|
export type UnsubackPacket = {
|
|
7
11
|
type: TPacketType;
|
|
8
12
|
id: PacketId;
|
|
@@ -4,6 +4,9 @@ import { BitMask } from "./BitMask.ts";
|
|
|
4
4
|
import { Encoder } from "./encoder.ts";
|
|
5
5
|
import { booleanFlag, Decoder, DecoderError } from "./decoder.ts";
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* UnsubscribePacket is sent from client to server to unsubscribe from topics
|
|
9
|
+
*/
|
|
7
10
|
export type UnsubscribePacket = {
|
|
8
11
|
type: TPacketType;
|
|
9
12
|
id: PacketId;
|
package/node/tcpClient.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/*
|
|
2
|
+
* this a NodeJS specific implementation of TCP client sockets
|
|
3
|
+
* it extends the platform agnostic Client class
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
3
6
|
import { Client } from "../client/client.ts";
|
|
4
7
|
import { logger } from "../client/deps.ts";
|
|
5
8
|
import { readFile } from "node:fs/promises";
|
|
@@ -8,6 +11,15 @@ import * as tls from "node:tls";
|
|
|
8
11
|
import type { SockConn } from "../socket/socket.ts";
|
|
9
12
|
import { wrapNodeSocket } from "./wrapNodeSocket.ts";
|
|
10
13
|
|
|
14
|
+
/**
|
|
15
|
+
* @function getFileData
|
|
16
|
+
* @param filename
|
|
17
|
+
* @returns Promise
|
|
18
|
+
*
|
|
19
|
+
* Fetches data from a file and returns it as a string
|
|
20
|
+
* @example
|
|
21
|
+
* const data = await getFileData("data.txt");
|
|
22
|
+
*/
|
|
11
23
|
export async function getFileData(filename: string | undefined) {
|
|
12
24
|
if (!filename) {
|
|
13
25
|
return;
|
|
@@ -19,6 +31,12 @@ export async function getFileData(filename: string | undefined) {
|
|
|
19
31
|
return data;
|
|
20
32
|
}
|
|
21
33
|
|
|
34
|
+
/*
|
|
35
|
+
* TCPclient extends the Client class to provide TCP based clients
|
|
36
|
+
* it is used by the MQTTclient to connect to the broker
|
|
37
|
+
* see mqtt.ts in the /bin folder as an example
|
|
38
|
+
*/
|
|
39
|
+
|
|
22
40
|
export class TcpClient extends Client {
|
|
23
41
|
protected connectMQTT(hostname: string, port = 1883): Promise<SockConn> {
|
|
24
42
|
logger.debug({ hostname, port });
|
package/node/tcpServer.test.ts
CHANGED
|
@@ -9,10 +9,10 @@ logger.level(LogLevel.info);
|
|
|
9
9
|
export function sleep(ms: number): Promise<unknown> {
|
|
10
10
|
return new Promise((r) => setTimeout(r, ms));
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
test("Test pubSub using client and server", async function () {
|
|
13
14
|
const server = new TcpServer({ port: 0 }, {});
|
|
14
15
|
server.start();
|
|
15
|
-
await sleep(1000);
|
|
16
16
|
|
|
17
17
|
assert.deepStrictEqual(
|
|
18
18
|
server.port !== undefined,
|
|
@@ -86,9 +86,64 @@ export async function serverTest() {
|
|
|
86
86
|
|
|
87
87
|
logger.verbose(`Stop server`);
|
|
88
88
|
server.stop();
|
|
89
|
-
}
|
|
89
|
+
});
|
|
90
90
|
|
|
91
|
-
test("Test
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
test("Test subscription persistence after reconnect", async function () {
|
|
92
|
+
// Start server
|
|
93
|
+
const server = new TcpServer({ port: 0 }, {});
|
|
94
|
+
server.start();
|
|
95
|
+
|
|
96
|
+
const params = {
|
|
97
|
+
url: new URL(`mqtt://${server.address}:${server.port}`),
|
|
98
|
+
numberOfRetries: 0,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const client = new TcpClient();
|
|
102
|
+
const testTopic = "test/topic";
|
|
103
|
+
const received: PublishPacket[] = [];
|
|
104
|
+
|
|
105
|
+
// First connection and subscription
|
|
106
|
+
await client.connect(params);
|
|
107
|
+
await client.subscribe({
|
|
108
|
+
subscriptions: [{
|
|
109
|
+
topicFilter: testTopic,
|
|
110
|
+
qos: 0,
|
|
111
|
+
}],
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Start receiving messages
|
|
115
|
+
(async function () {
|
|
116
|
+
for await (const item of client.messages()) {
|
|
117
|
+
received.push(item);
|
|
118
|
+
}
|
|
119
|
+
})();
|
|
120
|
+
|
|
121
|
+
// Disconnect client
|
|
122
|
+
await client.disconnect();
|
|
123
|
+
await sleep(100);
|
|
124
|
+
|
|
125
|
+
// Reconnect client
|
|
126
|
+
await client.connect(params);
|
|
127
|
+
await sleep(100);
|
|
128
|
+
|
|
129
|
+
// Publish test message
|
|
130
|
+
await client.publish({
|
|
131
|
+
topic: testTopic,
|
|
132
|
+
qos: 0,
|
|
133
|
+
payload: new Uint8Array([0x01]),
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
await sleep(100);
|
|
137
|
+
logger.verbose(`Disconnect client`);
|
|
138
|
+
await client.disconnect();
|
|
139
|
+
|
|
140
|
+
// Verify message was received
|
|
141
|
+
assert.equal(received.length, 1, "Should receive one message");
|
|
142
|
+
assert.equal(
|
|
143
|
+
received[0].topic,
|
|
144
|
+
testTopic,
|
|
145
|
+
"Should receive message on subscribed topic",
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
server.stop();
|
|
94
149
|
});
|
package/node/tcpServer.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/*
|
|
2
|
+
* This module provides a NodeJS specific implementation of a TCP socket listener
|
|
3
|
+
* it uses the platform agnostic MqttServer class
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
3
6
|
import type { Server } from "node:net";
|
|
4
7
|
import { createServer } from "node:net";
|
|
5
8
|
import type { MqttServerOptions } from "../server/mod.ts";
|
|
@@ -11,6 +14,9 @@ type ServerOptions = {
|
|
|
11
14
|
port?: number;
|
|
12
15
|
};
|
|
13
16
|
|
|
17
|
+
/*
|
|
18
|
+
* TCP server that wraps a MqttServer, see demoServer.ts in the /bin folder
|
|
19
|
+
*/
|
|
14
20
|
export class TcpServer {
|
|
15
21
|
private mqttServer: MqttServer;
|
|
16
22
|
private server?: Server;
|
|
@@ -20,11 +26,18 @@ export class TcpServer {
|
|
|
20
26
|
this.serverOptions = serverOptions;
|
|
21
27
|
}
|
|
22
28
|
|
|
23
|
-
start() {
|
|
29
|
+
async start() {
|
|
24
30
|
this.server = createServer((sock) =>
|
|
25
31
|
this.mqttServer.serve(wrapNodeSocket(sock))
|
|
26
32
|
);
|
|
33
|
+
const isListening = new Promise((resolve) => {
|
|
34
|
+
this.server?.on("listening", () => {
|
|
35
|
+
resolve(true);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
27
38
|
this.server.listen(this.serverOptions.port, this.serverOptions.hostname);
|
|
39
|
+
await isListening;
|
|
40
|
+
return;
|
|
28
41
|
}
|
|
29
42
|
stop() {
|
|
30
43
|
this.server?.close();
|
|
@@ -32,7 +45,7 @@ export class TcpServer {
|
|
|
32
45
|
|
|
33
46
|
get port() {
|
|
34
47
|
const address = this.server?.address();
|
|
35
|
-
if (typeof address === "object") {
|
|
48
|
+
if (typeof address === "object" && address !== null) {
|
|
36
49
|
return address?.port;
|
|
37
50
|
}
|
|
38
51
|
return this.serverOptions?.port;
|