@seriousme/opifex 1.6.3 → 1.7.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/package.json +12 -2
- package/.github/dependabot.yml +0 -7
- package/.github/workflows/closeStale.yml +0 -15
- package/.github/workflows/codeql-analysis.yml +0 -71
- package/.github/workflows/deno-ci.yml +0 -16
- package/.github/workflows/nodejs-ci.yml +0 -23
- package/.github/workflows/publish.yml +0 -16
- package/.github/workflows/update-license-year.sh +0 -14
- package/.github/workflows/update-license-year.yml +0 -13
- package/CHANGELOG.md +0 -80
- package/deno.json +0 -16
- package/deno.lock +0 -34
- package/dist/bin/conformanceServer.js +0 -97
- package/dist/bin/demoServer.js +0 -96
- package/dist/bin/mqtt.js +0 -256
- package/dist/client/ConnectionState.js +0 -16
- package/dist/client/client.js +0 -187
- package/dist/client/context.js +0 -152
- package/dist/client/deps.js +0 -5
- package/dist/client/handlers/handleConnack.js +0 -24
- package/dist/client/handlers/handlePacket.js +0 -57
- package/dist/client/handlers/handlePuback.js +0 -13
- package/dist/client/handlers/handlePubcomp.js +0 -16
- package/dist/client/handlers/handlePublish.js +0 -33
- package/dist/client/handlers/handlePubrec.js +0 -26
- package/dist/client/handlers/handlePubrel.js +0 -31
- package/dist/client/handlers/handleSuback.js +0 -14
- package/dist/client/handlers/handleUnsuback.js +0 -15
- package/dist/client/mod.js +0 -5
- package/dist/client/store/deps.js +0 -1
- package/dist/client/store/memoryStore.js +0 -58
- package/dist/client/store/store.js +0 -4
- package/dist/mqttConn/mqttConn.js +0 -104
- package/dist/mqttPacket/AuthenticationResult.js +0 -16
- package/dist/mqttPacket/BitMask.js +0 -10
- package/dist/mqttPacket/PacketType.js +0 -18
- package/dist/mqttPacket/connack.js +0 -27
- package/dist/mqttPacket/connect.js +0 -115
- package/dist/mqttPacket/decoder.js +0 -89
- package/dist/mqttPacket/disconnect.js +0 -14
- package/dist/mqttPacket/encoder.js +0 -56
- package/dist/mqttPacket/length.js +0 -43
- package/dist/mqttPacket/mod.js +0 -76
- package/dist/mqttPacket/pingreq.js +0 -15
- package/dist/mqttPacket/pingres.js +0 -14
- package/dist/mqttPacket/puback.js +0 -20
- package/dist/mqttPacket/pubcomp.js +0 -20
- package/dist/mqttPacket/publish.js +0 -51
- package/dist/mqttPacket/pubrec.js +0 -20
- package/dist/mqttPacket/pubrel.js +0 -20
- package/dist/mqttPacket/suback.js +0 -29
- package/dist/mqttPacket/subscribe.js +0 -52
- package/dist/mqttPacket/types.js +0 -1
- package/dist/mqttPacket/unsuback.js +0 -20
- package/dist/mqttPacket/unsubscribe.js +0 -36
- package/dist/mqttPacket/validators.js +0 -15
- package/dist/node/tcpClient.js +0 -81
- package/dist/node/tcpServer.js +0 -50
- package/dist/node/wrapNodeSocket.js +0 -56
- package/dist/persistence/deps.js +0 -3
- package/dist/persistence/memory/memoryPersistence.js +0 -118
- package/dist/persistence/persistence.js +0 -1
- package/dist/persistence/store.js +0 -1
- package/dist/server/context.js +0 -100
- package/dist/server/deps.js +0 -5
- package/dist/server/handlers/handleConnect.js +0 -80
- package/dist/server/handlers/handleDisconnect.js +0 -9
- package/dist/server/handlers/handlePacket.js +0 -65
- package/dist/server/handlers/handlePingreq.js +0 -11
- package/dist/server/handlers/handlePuback.js +0 -16
- package/dist/server/handlers/handlePubcomp.js +0 -14
- package/dist/server/handlers/handlePublish.js +0 -54
- package/dist/server/handlers/handlePubrec.js +0 -23
- package/dist/server/handlers/handlePubrel.js +0 -27
- package/dist/server/handlers/handleSubscribe.js +0 -56
- package/dist/server/handlers/handleUnsubscribe.js +0 -18
- package/dist/server/mod.js +0 -7
- package/dist/server/server.js +0 -47
- package/dist/socket/socket.js +0 -36
- package/dist/timer/timer.js +0 -40
- package/dist/trie/trie.js +0 -95
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/utils/args.js +0 -75
- package/dist/utils/assert.js +0 -20
- package/dist/utils/asyncQueue.js +0 -69
- package/dist/utils/deferred.js +0 -14
- package/dist/utils/logger.js +0 -58
- package/dist/utils/mod.js +0 -11
- package/dist/utils/nextTick.js +0 -18
- package/dist/utils/platform.js +0 -7
- package/tsconfig.json +0 -22
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Handles PUBACK packet processing for QoS level 1 PUBLISH responses
|
|
3
|
-
* @param ctx - The MQTT client context
|
|
4
|
-
* @param packet - The PUBACK packet received from the broker
|
|
5
|
-
* @description
|
|
6
|
-
* - Removes the packet ID from the list of pending outgoing messages
|
|
7
|
-
* - Completes the corresponding publish operation
|
|
8
|
-
*/
|
|
9
|
-
export function handlePuback(ctx, packet) {
|
|
10
|
-
const id = packet.id;
|
|
11
|
-
ctx.store.pendingOutgoing.delete(id);
|
|
12
|
-
ctx.receivePuback(id);
|
|
13
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Handles PUBCOMP packets which are the response to PUBREL packets in QoS 2 flow
|
|
3
|
-
* @param ctx - The connection context containing the client state and store
|
|
4
|
-
* @param packet - The PUBCOMP packet received from the broker
|
|
5
|
-
* @description
|
|
6
|
-
* This function processes PUBCOMP packets which are the fourth and final packet
|
|
7
|
-
* in the QoS 2 protocol exchange. It removes the message from pendingAckOutgoing queue
|
|
8
|
-
* Completes the corresponding publish operation
|
|
9
|
-
*/
|
|
10
|
-
export function handlePubcomp(ctx, packet) {
|
|
11
|
-
const id = packet.id;
|
|
12
|
-
if (ctx.store.pendingAckOutgoing.has(id)) {
|
|
13
|
-
ctx.store.pendingAckOutgoing.delete(id);
|
|
14
|
-
ctx.receivePubcomp(id);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { PacketType } from "../deps.js";
|
|
2
|
-
/**
|
|
3
|
-
* Handles incoming MQTT publish packets based on their QoS level
|
|
4
|
-
* @param ctx - The MQTT connection context
|
|
5
|
-
* @param packet - The incoming publish packet to handle
|
|
6
|
-
* @returns Promise that resolves when packet is handled
|
|
7
|
-
*/
|
|
8
|
-
export async function handlePublish(ctx, packet) {
|
|
9
|
-
const qos = packet.qos || 0;
|
|
10
|
-
if (qos === 0) {
|
|
11
|
-
ctx.receivePublish(packet);
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
if (packet.id !== undefined) {
|
|
15
|
-
// qos 1
|
|
16
|
-
if (qos === 1) {
|
|
17
|
-
ctx.receivePublish(packet);
|
|
18
|
-
await ctx.send({
|
|
19
|
-
type: PacketType.puback,
|
|
20
|
-
id: packet.id,
|
|
21
|
-
});
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
// qos 2
|
|
25
|
-
if (ctx.store) {
|
|
26
|
-
ctx.store.pendingIncoming.set(packet.id, packet);
|
|
27
|
-
await ctx.send({
|
|
28
|
-
type: PacketType.pubrec,
|
|
29
|
-
id: packet.id,
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { PacketType } from "../deps.js";
|
|
2
|
-
/**
|
|
3
|
-
* Handles PUBREC (Publish Received) packets for QoS 2 message flow
|
|
4
|
-
* It is the second packet of the QoS 2 protocol exchange.
|
|
5
|
-
* @param ctx - The connection context containing message stores and send function
|
|
6
|
-
* @param packet - The received PUBREC packet
|
|
7
|
-
* @description
|
|
8
|
-
* When a PUBREC packet is received:
|
|
9
|
-
* 1. Checks if there is a pending outgoing message with matching packet ID
|
|
10
|
-
* 2. Creates a PUBREL packet to acknowledge the PUBREC
|
|
11
|
-
* 3. Add the packet ID to pendingAckOutgoing store
|
|
12
|
-
* 4. Removes the message from pendingOutgoing store
|
|
13
|
-
* 5. Sends the PUBREL packet
|
|
14
|
-
*/
|
|
15
|
-
export async function handlePubrec(ctx, packet) {
|
|
16
|
-
const id = packet.id;
|
|
17
|
-
const ack = {
|
|
18
|
-
type: PacketType.pubrel,
|
|
19
|
-
id,
|
|
20
|
-
};
|
|
21
|
-
if (ctx.store.pendingOutgoing.has(id)) {
|
|
22
|
-
ctx.store.pendingAckOutgoing.set(id, ack);
|
|
23
|
-
ctx.store.pendingOutgoing.delete(id);
|
|
24
|
-
await ctx.send(ack);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { PacketType } from "../deps.js";
|
|
2
|
-
/**
|
|
3
|
-
* Handles a PUBREL packet in the MQTT QoS 2 flow
|
|
4
|
-
* It is the third packet of the QoS 2 protocol exchange
|
|
5
|
-
* and a response to the PUBREC packet
|
|
6
|
-
*
|
|
7
|
-
* It uses Method A from the MQTT specification
|
|
8
|
-
* @param ctx - The MQTT client context
|
|
9
|
-
* @param packet - The PUBREL packet received
|
|
10
|
-
* @description
|
|
11
|
-
* When a PUBREL packet is received:
|
|
12
|
-
* 1. If there is a matching pending incoming message:
|
|
13
|
-
* - Delivers the stored message to the application
|
|
14
|
-
* - Removes the message from pending incoming store
|
|
15
|
-
* - Sends PUBCOMP packet as acknowledgement
|
|
16
|
-
* 2. If no matching message found, silently ignores
|
|
17
|
-
*/
|
|
18
|
-
export async function handlePubrel(ctx, packet) {
|
|
19
|
-
const id = packet.id;
|
|
20
|
-
if (ctx.store.pendingIncoming.has(id)) {
|
|
21
|
-
const storedPacket = ctx.store.pendingIncoming.get(id);
|
|
22
|
-
if (storedPacket) {
|
|
23
|
-
ctx.receivePublish(storedPacket);
|
|
24
|
-
ctx.store.pendingIncoming.delete(id);
|
|
25
|
-
await ctx.send({
|
|
26
|
-
type: PacketType.pubcomp,
|
|
27
|
-
id,
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Handles SUBACK packets received from the server
|
|
3
|
-
* @param ctx - The MQTT client context
|
|
4
|
-
* @param packet - The SUBACK packet containing subscription confirmations
|
|
5
|
-
* @description
|
|
6
|
-
* - Processes SUBACK packets sent by server to confirm SUBSCRIBE requests
|
|
7
|
-
* - Removes the packet ID from pending outgoing messages
|
|
8
|
-
* - Notifies the client about granted QoS levels for each subscription
|
|
9
|
-
*/
|
|
10
|
-
export function handleSuback(ctx, packet) {
|
|
11
|
-
const id = packet.id;
|
|
12
|
-
ctx.store.pendingOutgoing.delete(id);
|
|
13
|
-
ctx.receiveSuback(id, packet.returnCodes);
|
|
14
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Handles the UNSUBACK packet received from the server
|
|
3
|
-
* @param ctx - The MQTT client context
|
|
4
|
-
* @param packet - The UNSUBACK packet received from the server
|
|
5
|
-
* @description
|
|
6
|
-
* The UNSUBACK Packet is sent by the Server to the Client to confirm receipt of
|
|
7
|
-
* an UNSUBSCRIBE Packet.
|
|
8
|
-
* When received, it removes the pending outgoing request and completes the corresponding
|
|
9
|
-
* unsubscribe operation.
|
|
10
|
-
*/
|
|
11
|
-
export function handleUnsuback(ctx, packet) {
|
|
12
|
-
const id = packet.id;
|
|
13
|
-
ctx.store.pendingOutgoing.delete(id);
|
|
14
|
-
ctx.receiveUnsuback(id);
|
|
15
|
-
}
|
package/dist/client/mod.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { assert } from "../../utils/mod.js";
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module memoryStore
|
|
3
|
-
*/
|
|
4
|
-
import { assert } from "./deps.js";
|
|
5
|
-
import { maxPacketId, } from "./store.js";
|
|
6
|
-
/**
|
|
7
|
-
* In-memory implementation of the IStore interface for managing MQTT packet storage
|
|
8
|
-
*/
|
|
9
|
-
export class MemoryStore {
|
|
10
|
-
/** Current packet ID counter */
|
|
11
|
-
packetId;
|
|
12
|
-
/** Map of pending incoming packets */
|
|
13
|
-
pendingIncoming;
|
|
14
|
-
/** Map of pending outgoing packets */
|
|
15
|
-
pendingOutgoing;
|
|
16
|
-
/** Map of pending acknowledgment outgoing packets */
|
|
17
|
-
pendingAckOutgoing;
|
|
18
|
-
/**
|
|
19
|
-
* Creates a new MemoryStore instance
|
|
20
|
-
*/
|
|
21
|
-
constructor() {
|
|
22
|
-
this.packetId = 0;
|
|
23
|
-
this.pendingIncoming = new Map();
|
|
24
|
-
this.pendingOutgoing = new Map();
|
|
25
|
-
this.pendingAckOutgoing = new Map();
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Generates the next available packet ID
|
|
29
|
-
* @returns {PacketId} The next available packet ID
|
|
30
|
-
* @throws {Error} If no unused packet ID is available
|
|
31
|
-
*/
|
|
32
|
-
nextId() {
|
|
33
|
-
const currentId = this.packetId;
|
|
34
|
-
do {
|
|
35
|
-
this.packetId++;
|
|
36
|
-
if (this.packetId > maxPacketId) {
|
|
37
|
-
this.packetId = 1;
|
|
38
|
-
}
|
|
39
|
-
} while ((this.pendingIncoming.has(this.packetId) ||
|
|
40
|
-
this.pendingOutgoing.has(this.packetId) ||
|
|
41
|
-
this.pendingAckOutgoing.has(this.packetId)) &&
|
|
42
|
-
this.packetId !== currentId);
|
|
43
|
-
assert(this.packetId !== currentId, "No unused packetId available");
|
|
44
|
-
return this.packetId;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Asynchronously yields all pending outgoing packets
|
|
48
|
-
* @yields {PendingOutgoingPackets} Pending outgoing packets
|
|
49
|
-
*/
|
|
50
|
-
async *pendingOutgoingPackets() {
|
|
51
|
-
for (const [_id, packet] of this.pendingAckOutgoing) {
|
|
52
|
-
yield packet;
|
|
53
|
-
}
|
|
54
|
-
for (const [_id, packet] of this.pendingOutgoing) {
|
|
55
|
-
yield packet;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { decodePayload, encode, getLengthDecoder, } from "../mqttPacket/mod.js";
|
|
2
|
-
import { assert } from "../utils/mod.js";
|
|
3
|
-
import { Conn } from "../socket/socket.js";
|
|
4
|
-
export const MqttConnError = {
|
|
5
|
-
invalidPacket: "Invalid Packet",
|
|
6
|
-
packetTooLarge: "Packet too large",
|
|
7
|
-
UnexpectedEof: "Unexpected EOF",
|
|
8
|
-
};
|
|
9
|
-
async function readByte(conn) {
|
|
10
|
-
const buf = new Uint8Array(1);
|
|
11
|
-
const bytesRead = await conn.read(buf);
|
|
12
|
-
assert(bytesRead !== null, MqttConnError.UnexpectedEof);
|
|
13
|
-
assert(bytesRead !== 0, MqttConnError.UnexpectedEof);
|
|
14
|
-
return buf[0];
|
|
15
|
-
}
|
|
16
|
-
async function readFull(conn, buf) {
|
|
17
|
-
let bytesRead = 0;
|
|
18
|
-
while (bytesRead < buf.length) {
|
|
19
|
-
const read = await conn.read(buf.subarray(bytesRead));
|
|
20
|
-
assert(read !== null, MqttConnError.UnexpectedEof);
|
|
21
|
-
assert(read !== 0, MqttConnError.UnexpectedEof);
|
|
22
|
-
bytesRead += read;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
/** Read MQTT packet
|
|
26
|
-
* @throws `Error` if packet is invalid
|
|
27
|
-
*/
|
|
28
|
-
export async function readPacket(conn, maxPacketSize) {
|
|
29
|
-
// fixed header is 1 byte of type + flags
|
|
30
|
-
// + a maximum of 4 bytes to encode the remaining length
|
|
31
|
-
const decodeLength = getLengthDecoder();
|
|
32
|
-
const firstByte = await readByte(conn);
|
|
33
|
-
let result;
|
|
34
|
-
do {
|
|
35
|
-
const byte = await readByte(conn);
|
|
36
|
-
result = decodeLength(byte);
|
|
37
|
-
} while (!result.done);
|
|
38
|
-
const remainingLength = result.length;
|
|
39
|
-
assert(remainingLength < maxPacketSize - 1, MqttConnError.packetTooLarge);
|
|
40
|
-
const packetBuf = new Uint8Array(remainingLength);
|
|
41
|
-
// read the rest of the packet
|
|
42
|
-
await readFull(conn, packetBuf);
|
|
43
|
-
const packet = decodePayload(firstByte, packetBuf);
|
|
44
|
-
assert(packet !== null, MqttConnError.UnexpectedEof);
|
|
45
|
-
return packet;
|
|
46
|
-
}
|
|
47
|
-
export class MqttConn {
|
|
48
|
-
conn;
|
|
49
|
-
maxPacketSize;
|
|
50
|
-
_reason = undefined;
|
|
51
|
-
_isClosed = false;
|
|
52
|
-
constructor({ conn, maxPacketSize, }) {
|
|
53
|
-
this.conn = new Conn(conn);
|
|
54
|
-
this.maxPacketSize = maxPacketSize || 2 * 1024 * 1024;
|
|
55
|
-
}
|
|
56
|
-
get reason() {
|
|
57
|
-
return this._reason;
|
|
58
|
-
}
|
|
59
|
-
async *[Symbol.asyncIterator]() {
|
|
60
|
-
while (!this._isClosed) {
|
|
61
|
-
try {
|
|
62
|
-
yield await readPacket(this.conn, this.maxPacketSize);
|
|
63
|
-
}
|
|
64
|
-
catch (err) {
|
|
65
|
-
if (err instanceof Error) {
|
|
66
|
-
if (err.name === "PartialReadError") {
|
|
67
|
-
err.message = MqttConnError.UnexpectedEof;
|
|
68
|
-
}
|
|
69
|
-
this._reason = err.message;
|
|
70
|
-
}
|
|
71
|
-
// packet too large, malformed packet or connection closed
|
|
72
|
-
this.close();
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
async send(data) {
|
|
78
|
-
try {
|
|
79
|
-
await this.conn.write(encode(data));
|
|
80
|
-
}
|
|
81
|
-
catch (err) {
|
|
82
|
-
if (err instanceof Error) {
|
|
83
|
-
this._reason = err.message;
|
|
84
|
-
}
|
|
85
|
-
this.close();
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
get isClosed() {
|
|
89
|
-
return this._isClosed;
|
|
90
|
-
}
|
|
91
|
-
close() {
|
|
92
|
-
if (this.isClosed)
|
|
93
|
-
return;
|
|
94
|
-
try {
|
|
95
|
-
this.conn.close();
|
|
96
|
-
}
|
|
97
|
-
catch (e) {
|
|
98
|
-
console.error(e);
|
|
99
|
-
}
|
|
100
|
-
finally {
|
|
101
|
-
this._isClosed = true;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Possible MQTT authentication results
|
|
3
|
-
*/
|
|
4
|
-
export const AuthenticationResult = {
|
|
5
|
-
ok: 0,
|
|
6
|
-
unacceptableProtocol: 1,
|
|
7
|
-
rejectedUsername: 2,
|
|
8
|
-
serverUnavailable: 3,
|
|
9
|
-
badUsernameOrPassword: 4,
|
|
10
|
-
notAuthorized: 5,
|
|
11
|
-
};
|
|
12
|
-
/**
|
|
13
|
-
* Reverse lookup for AuthenticationResult
|
|
14
|
-
*/
|
|
15
|
-
export const AuthenticationResultByNumber = Object
|
|
16
|
-
.fromEntries(Object.entries(AuthenticationResult).map(([k, v]) => [v, k]));
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export const PacketType = {
|
|
2
|
-
reserved: 0,
|
|
3
|
-
connect: 1,
|
|
4
|
-
connack: 2,
|
|
5
|
-
publish: 3,
|
|
6
|
-
puback: 4,
|
|
7
|
-
pubrec: 5,
|
|
8
|
-
pubrel: 6,
|
|
9
|
-
pubcomp: 7,
|
|
10
|
-
subscribe: 8,
|
|
11
|
-
suback: 9,
|
|
12
|
-
unsubscribe: 10,
|
|
13
|
-
unsuback: 11,
|
|
14
|
-
pingreq: 12,
|
|
15
|
-
pingres: 13,
|
|
16
|
-
disconnect: 14,
|
|
17
|
-
};
|
|
18
|
-
export const PacketNameByType = Object.fromEntries(Object.entries(PacketType).map(([k, v]) => [v, k]));
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { PacketType } from "./PacketType.js";
|
|
2
|
-
import { BitMask } from "./BitMask.js";
|
|
3
|
-
import { booleanFlag, Decoder, DecoderError } from "./decoder.js";
|
|
4
|
-
import { AuthenticationResultByNumber } from "./AuthenticationResult.js";
|
|
5
|
-
export const connack = {
|
|
6
|
-
encode(packet) {
|
|
7
|
-
const flags = 0;
|
|
8
|
-
return {
|
|
9
|
-
flags,
|
|
10
|
-
bytes: [packet.sessionPresent ? 1 : 0, packet.returnCode || 0],
|
|
11
|
-
};
|
|
12
|
-
},
|
|
13
|
-
decode(buffer, _flags) {
|
|
14
|
-
const decoder = new Decoder(buffer);
|
|
15
|
-
const sessionPresent = booleanFlag(decoder.getByte(), BitMask.bit0);
|
|
16
|
-
const returnCode = decoder.getByte();
|
|
17
|
-
decoder.done();
|
|
18
|
-
if (!AuthenticationResultByNumber[returnCode]) {
|
|
19
|
-
throw new DecoderError("Invalid return code");
|
|
20
|
-
}
|
|
21
|
-
return {
|
|
22
|
-
type: PacketType.connack,
|
|
23
|
-
sessionPresent,
|
|
24
|
-
returnCode,
|
|
25
|
-
};
|
|
26
|
-
},
|
|
27
|
-
};
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { PacketType } from "./PacketType.js";
|
|
2
|
-
import { BitMask } from "./BitMask.js";
|
|
3
|
-
import { Encoder } from "./encoder.js";
|
|
4
|
-
import { booleanFlag, Decoder, DecoderError, hasEmptyFlags, } from "./decoder.js";
|
|
5
|
-
function invalidProtocolName(version, name) {
|
|
6
|
-
if (version === 4 && name !== "MQTT") {
|
|
7
|
-
return true;
|
|
8
|
-
}
|
|
9
|
-
return false;
|
|
10
|
-
}
|
|
11
|
-
export const connect = {
|
|
12
|
-
encode(packet) {
|
|
13
|
-
const flags = 0;
|
|
14
|
-
const protocolLevel = 4;
|
|
15
|
-
const protocolName = protocolLevel === 4 ? "MQTT" : "MQIsdp";
|
|
16
|
-
const clientId = packet.clientId || "";
|
|
17
|
-
const usernameFlag = !!packet.username;
|
|
18
|
-
const passwordFlag = !!packet.password;
|
|
19
|
-
const willRetain = !!packet.will?.retain;
|
|
20
|
-
const willQoS = packet.will?.qos || 0;
|
|
21
|
-
const willFlag = !!packet.will;
|
|
22
|
-
const cleanSession = packet.clean !== false;
|
|
23
|
-
const connectFlags = (usernameFlag ? BitMask.bit7 : 0) +
|
|
24
|
-
(passwordFlag ? BitMask.bit6 : 0) +
|
|
25
|
-
(willRetain ? BitMask.bit5 : 0) +
|
|
26
|
-
(willQoS & 2 ? BitMask.bit4 : 0) +
|
|
27
|
-
(willQoS & 1 ? BitMask.bit3 : 0) +
|
|
28
|
-
(willFlag ? BitMask.bit2 : 0) +
|
|
29
|
-
(cleanSession ? BitMask.bit1 : 0);
|
|
30
|
-
const keepAlive = packet.keepAlive || 0;
|
|
31
|
-
const encoder = new Encoder();
|
|
32
|
-
encoder
|
|
33
|
-
.setUtf8String(protocolName)
|
|
34
|
-
.setByte(protocolLevel)
|
|
35
|
-
.setByte(connectFlags)
|
|
36
|
-
.setInt16(keepAlive)
|
|
37
|
-
.setUtf8String(clientId);
|
|
38
|
-
if (packet.will) {
|
|
39
|
-
encoder.setTopic(packet.will.topic).setByteArray(packet.will.payload);
|
|
40
|
-
}
|
|
41
|
-
if (packet.username) {
|
|
42
|
-
encoder.setUtf8String(packet.username);
|
|
43
|
-
}
|
|
44
|
-
if (packet.password) {
|
|
45
|
-
encoder.setByteArray(packet.password);
|
|
46
|
-
}
|
|
47
|
-
return { flags, bytes: encoder.done() };
|
|
48
|
-
},
|
|
49
|
-
decode(buffer, flags) {
|
|
50
|
-
const decoder = new Decoder(buffer);
|
|
51
|
-
const protocolName = decoder.getUtf8String();
|
|
52
|
-
const protocolLevel = decoder.getByte();
|
|
53
|
-
if (invalidProtocolName(protocolLevel, protocolName)) {
|
|
54
|
-
throw new DecoderError("Invalid protocol name");
|
|
55
|
-
}
|
|
56
|
-
const connectFlags = decoder.getByte();
|
|
57
|
-
const usernameFlag = booleanFlag(connectFlags, BitMask.bit7);
|
|
58
|
-
const passwordFlag = booleanFlag(connectFlags, BitMask.bit6);
|
|
59
|
-
const willRetain = booleanFlag(connectFlags, BitMask.bit5);
|
|
60
|
-
const willQoS = (connectFlags & (BitMask.bit4 + BitMask.bit3)) >> 3;
|
|
61
|
-
const willFlag = booleanFlag(connectFlags, BitMask.bit2);
|
|
62
|
-
const cleanSession = booleanFlag(connectFlags, BitMask.bit1);
|
|
63
|
-
const reservedBit = booleanFlag(connectFlags, BitMask.bit0);
|
|
64
|
-
hasEmptyFlags(flags);
|
|
65
|
-
// The Server MUST validate that the reserved flag in the CONNECT Control Packet
|
|
66
|
-
// is set to zero and disconnect the Client if it is not zero [MQTT-3.1.2-3].
|
|
67
|
-
if (reservedBit) {
|
|
68
|
-
throw new DecoderError("Invalid reserved bit");
|
|
69
|
-
}
|
|
70
|
-
if (willQoS !== 0 && willQoS !== 1 && willQoS !== 2) {
|
|
71
|
-
throw new DecoderError("Invalid will qos");
|
|
72
|
-
}
|
|
73
|
-
const keepAlive = decoder.getInt16();
|
|
74
|
-
const clientId = decoder.getUtf8String();
|
|
75
|
-
let willTopic;
|
|
76
|
-
let willPayload;
|
|
77
|
-
if (willFlag) {
|
|
78
|
-
willTopic = decoder.getTopic();
|
|
79
|
-
willPayload = decoder.getByteArray();
|
|
80
|
-
}
|
|
81
|
-
let username;
|
|
82
|
-
let password;
|
|
83
|
-
if (usernameFlag) {
|
|
84
|
-
username = decoder.getUtf8String();
|
|
85
|
-
}
|
|
86
|
-
if (passwordFlag) {
|
|
87
|
-
password = decoder.getByteArray();
|
|
88
|
-
}
|
|
89
|
-
decoder.done();
|
|
90
|
-
if (!willFlag && (willQoS !== 0 || willRetain === true)) {
|
|
91
|
-
throw new DecoderError("Will QoS must be 0 and Will retain to false when Will flag is false");
|
|
92
|
-
}
|
|
93
|
-
if (clientId.length === 0 && cleanSession === false) {
|
|
94
|
-
throw new DecoderError("Clean session must be true if clientID is empty");
|
|
95
|
-
}
|
|
96
|
-
return {
|
|
97
|
-
type: PacketType.connect,
|
|
98
|
-
protocolName: protocolName,
|
|
99
|
-
protocolLevel,
|
|
100
|
-
clientId: clientId,
|
|
101
|
-
username: username ? username : undefined,
|
|
102
|
-
password: password ? password : undefined,
|
|
103
|
-
will: willFlag
|
|
104
|
-
? {
|
|
105
|
-
topic: willTopic || "",
|
|
106
|
-
payload: willPayload || Uint8Array.from([0]),
|
|
107
|
-
retain: willRetain,
|
|
108
|
-
qos: willQoS,
|
|
109
|
-
}
|
|
110
|
-
: undefined,
|
|
111
|
-
clean: cleanSession,
|
|
112
|
-
keepAlive,
|
|
113
|
-
};
|
|
114
|
-
},
|
|
115
|
-
};
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { invalidTopic, invalidTopicFilter } from "./validators.js";
|
|
2
|
-
const utf8Decoder = new TextDecoder("utf-8");
|
|
3
|
-
export function booleanFlag(byte, mask) {
|
|
4
|
-
return !!(byte & mask);
|
|
5
|
-
}
|
|
6
|
-
export function isEmptyBuf(buf) {
|
|
7
|
-
if (buf.length > 0) {
|
|
8
|
-
throw new DecoderError("Packet too long");
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
export function hasEmptyFlags(flags) {
|
|
12
|
-
if (flags !== 0) {
|
|
13
|
-
throw new DecoderError("Invalid fixed header flags");
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
export class DecoderError extends Error {
|
|
17
|
-
constructor(message) {
|
|
18
|
-
super(message);
|
|
19
|
-
this.name = "DecoderError";
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
export class Decoder {
|
|
23
|
-
buf;
|
|
24
|
-
pos;
|
|
25
|
-
len;
|
|
26
|
-
constructor(buf, pos = 0) {
|
|
27
|
-
this.buf = buf;
|
|
28
|
-
this.pos = pos;
|
|
29
|
-
this.len = buf.length;
|
|
30
|
-
}
|
|
31
|
-
checkpos(pos) {
|
|
32
|
-
if (pos > this.len) {
|
|
33
|
-
throw new DecoderError("Packet too short");
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
getByte() {
|
|
37
|
-
this.checkpos(this.pos);
|
|
38
|
-
return this.buf[this.pos++];
|
|
39
|
-
}
|
|
40
|
-
getInt16() {
|
|
41
|
-
const msb = this.getByte();
|
|
42
|
-
const lsb = this.getByte();
|
|
43
|
-
return (msb << 8) | lsb;
|
|
44
|
-
}
|
|
45
|
-
getByteArray() {
|
|
46
|
-
const len = this.getInt16();
|
|
47
|
-
const start = this.pos;
|
|
48
|
-
const end = this.pos + len;
|
|
49
|
-
this.pos = end;
|
|
50
|
-
this.checkpos(end);
|
|
51
|
-
return this.buf.subarray(start, end);
|
|
52
|
-
}
|
|
53
|
-
getUtf8String() {
|
|
54
|
-
const str = utf8Decoder.decode(this.getByteArray());
|
|
55
|
-
return str;
|
|
56
|
-
}
|
|
57
|
-
getTopic() {
|
|
58
|
-
const topic = this.getUtf8String();
|
|
59
|
-
if (invalidTopic(topic)) {
|
|
60
|
-
throw new DecoderError("Topic must contain valid UTF-8 and contain more than 1 byte and no wildcards");
|
|
61
|
-
}
|
|
62
|
-
return topic;
|
|
63
|
-
}
|
|
64
|
-
getTopicFilter() {
|
|
65
|
-
const topicFilter = this.getUtf8String();
|
|
66
|
-
if (invalidTopicFilter(topicFilter)) {
|
|
67
|
-
throw new DecoderError("Topicfilter must contain valid UTF-8 and contain more than 1 byte and valid wildcards");
|
|
68
|
-
}
|
|
69
|
-
return topicFilter;
|
|
70
|
-
}
|
|
71
|
-
getRemainder() {
|
|
72
|
-
const start = this.pos;
|
|
73
|
-
const end = this.len;
|
|
74
|
-
this.pos = end;
|
|
75
|
-
return this.buf.subarray(start, end);
|
|
76
|
-
}
|
|
77
|
-
atEnd() {
|
|
78
|
-
if (this.len === this.pos) {
|
|
79
|
-
return true;
|
|
80
|
-
}
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
done() {
|
|
84
|
-
if (this.atEnd()) {
|
|
85
|
-
return true;
|
|
86
|
-
}
|
|
87
|
-
throw new DecoderError("Packet too long");
|
|
88
|
-
}
|
|
89
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { PacketType } from "./PacketType.js";
|
|
2
|
-
import { isEmptyBuf } from "./decoder.js";
|
|
3
|
-
export const disconnect = {
|
|
4
|
-
encode(_packet) {
|
|
5
|
-
const flags = 0;
|
|
6
|
-
return { flags, bytes: [] };
|
|
7
|
-
},
|
|
8
|
-
decode(buffer, _flags) {
|
|
9
|
-
isEmptyBuf(buffer);
|
|
10
|
-
return {
|
|
11
|
-
type: PacketType.disconnect,
|
|
12
|
-
};
|
|
13
|
-
},
|
|
14
|
-
};
|