@seriousme/opifex 1.6.2 → 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/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/tcpServer.test.ts +137 -2
- package/node/tcpServer.test.ts +61 -6
- package/node/tcpServer.ts +9 -2
- package/node/wrapNodeSocket.ts +13 -2
- package/package.json +13 -3
- 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/asyncQueue.ts +2 -1
- package/utils/nextTick.ts +13 -1
- 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 -74
- package/deno.json +0 -15
- package/deno.lock +0 -34
- package/dist/bin/conformanceServer.js +0 -51
- package/dist/bin/demoServer.js +0 -46
- package/dist/bin/mqtt.js +0 -227
- package/dist/client/ConnectionState.js +0 -7
- package/dist/client/client.js +0 -132
- package/dist/client/context.js +0 -148
- package/dist/client/deps.js +0 -5
- package/dist/client/handlers/handleConnack.js +0 -18
- package/dist/client/handlers/handlePacket.js +0 -50
- package/dist/client/handlers/handlePuback.js +0 -6
- package/dist/client/handlers/handlePubcomp.js +0 -9
- package/dist/client/handlers/handlePublish.js +0 -28
- package/dist/client/handlers/handlePubrec.js +0 -17
- package/dist/client/handlers/handlePubrel.js +0 -19
- package/dist/client/handlers/handleSuback.js +0 -9
- package/dist/client/handlers/handleUnsuback.js +0 -7
- package/dist/client/mod.js +0 -5
- package/dist/client/store/deps.js +0 -1
- package/dist/client/store/memoryStore.js +0 -36
- package/dist/client/store/store.js +0 -1
- 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 -43
- package/dist/node/wrapNodeSocket.js +0 -47
- package/dist/persistence/deps.js +0 -3
- package/dist/persistence/memory/memoryPersistence.js +0 -117
- 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 -46
- package/dist/server/handlers/handleDisconnect.js +0 -4
- package/dist/server/handlers/handlePacket.js +0 -58
- package/dist/server/handlers/handlePingreq.js +0 -6
- package/dist/server/handlers/handlePuback.js +0 -8
- package/dist/server/handlers/handlePubcomp.js +0 -8
- package/dist/server/handlers/handlePublish.js +0 -41
- package/dist/server/handlers/handlePubrec.js +0 -15
- package/dist/server/handlers/handlePubrel.js +0 -18
- package/dist/server/handlers/handleSubscribe.js +0 -33
- package/dist/server/handlers/handleUnsubscribe.js +0 -12
- 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 -67
- 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 -6
- package/dist/utils/platform.js +0 -7
- package/tsconfig.json +0 -22
package/dist/client/mod.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { assert } from "../../utils/mod.js";
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { assert } from "./deps.js";
|
|
2
|
-
import { maxPacketId, } from "./store.js";
|
|
3
|
-
export class MemoryStore {
|
|
4
|
-
packetId;
|
|
5
|
-
pendingIncoming;
|
|
6
|
-
pendingOutgoing;
|
|
7
|
-
pendingAckOutgoing;
|
|
8
|
-
constructor() {
|
|
9
|
-
this.packetId = 0;
|
|
10
|
-
this.pendingIncoming = new Map();
|
|
11
|
-
this.pendingOutgoing = new Map();
|
|
12
|
-
this.pendingAckOutgoing = new Map();
|
|
13
|
-
}
|
|
14
|
-
nextId() {
|
|
15
|
-
const currentId = this.packetId;
|
|
16
|
-
do {
|
|
17
|
-
this.packetId++;
|
|
18
|
-
if (this.packetId > maxPacketId) {
|
|
19
|
-
this.packetId = 1;
|
|
20
|
-
}
|
|
21
|
-
} while ((this.pendingIncoming.has(this.packetId) ||
|
|
22
|
-
this.pendingOutgoing.has(this.packetId) ||
|
|
23
|
-
this.pendingAckOutgoing.has(this.packetId)) &&
|
|
24
|
-
this.packetId !== currentId);
|
|
25
|
-
assert(this.packetId !== currentId, "No unused packetId available");
|
|
26
|
-
return this.packetId;
|
|
27
|
-
}
|
|
28
|
-
async *pendingOutgoingPackets() {
|
|
29
|
-
for (const [_id, packet] of this.pendingAckOutgoing) {
|
|
30
|
-
yield packet;
|
|
31
|
-
}
|
|
32
|
-
for (const [_id, packet] of this.pendingOutgoing) {
|
|
33
|
-
yield packet;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const maxPacketId = 0xffff;
|
|
@@ -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
|
-
};
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
const utf8Encoder = new TextEncoder();
|
|
2
|
-
import { invalidTopic, invalidTopicFilter } from "./validators.js";
|
|
3
|
-
export class EncoderError extends Error {
|
|
4
|
-
constructor(message) {
|
|
5
|
-
super(message);
|
|
6
|
-
this.name = "EncoderError";
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
export class Encoder {
|
|
10
|
-
buffer;
|
|
11
|
-
constructor() {
|
|
12
|
-
this.buffer = [];
|
|
13
|
-
}
|
|
14
|
-
setByte(value) {
|
|
15
|
-
this.buffer.push(value);
|
|
16
|
-
return this;
|
|
17
|
-
}
|
|
18
|
-
setInt16(value) {
|
|
19
|
-
this.setByte(value >> 8);
|
|
20
|
-
this.setByte(value & 0xff);
|
|
21
|
-
return this;
|
|
22
|
-
}
|
|
23
|
-
setByteArray(value) {
|
|
24
|
-
if (value.length > 0xffff) {
|
|
25
|
-
throw new EncoderError("More than 0xffff bytes of data");
|
|
26
|
-
}
|
|
27
|
-
this.setInt16(value.length);
|
|
28
|
-
this.buffer.push(...value);
|
|
29
|
-
return this;
|
|
30
|
-
}
|
|
31
|
-
setUtf8String(value) {
|
|
32
|
-
this.setByteArray(utf8Encoder.encode(value));
|
|
33
|
-
return this;
|
|
34
|
-
}
|
|
35
|
-
setTopic(value) {
|
|
36
|
-
if (invalidTopic(value)) {
|
|
37
|
-
throw new EncoderError("Topic must contain valid UTF-8 and contain more than 1 byte and no wildcards");
|
|
38
|
-
}
|
|
39
|
-
this.setUtf8String(value);
|
|
40
|
-
return this;
|
|
41
|
-
}
|
|
42
|
-
setTopicFilter(value) {
|
|
43
|
-
if (invalidTopicFilter(value)) {
|
|
44
|
-
throw new EncoderError("Topicfilter must contain valid UTF-8 and contain more than 1 byte and valid wildcards");
|
|
45
|
-
}
|
|
46
|
-
this.setUtf8String(value);
|
|
47
|
-
return this;
|
|
48
|
-
}
|
|
49
|
-
setRemainder(value) {
|
|
50
|
-
this.buffer.push(...value);
|
|
51
|
-
return this;
|
|
52
|
-
}
|
|
53
|
-
done() {
|
|
54
|
-
return this.buffer;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
export function encodeLength(n) {
|
|
2
|
-
const output = [];
|
|
3
|
-
let x = n;
|
|
4
|
-
do {
|
|
5
|
-
let encodedByte = x % 0x80;
|
|
6
|
-
x = Math.floor(x / 0x80);
|
|
7
|
-
if (x > 0) {
|
|
8
|
-
encodedByte = encodedByte | 0x80;
|
|
9
|
-
}
|
|
10
|
-
output.push(encodedByte);
|
|
11
|
-
} while (x > 0);
|
|
12
|
-
if (output.length > 4) {
|
|
13
|
-
throw Error("length encoding failed");
|
|
14
|
-
}
|
|
15
|
-
return output;
|
|
16
|
-
}
|
|
17
|
-
export function decodeLength(buf, start) {
|
|
18
|
-
const decode = getLengthDecoder();
|
|
19
|
-
const lenBuf = buf.subarray(start);
|
|
20
|
-
for (const byte of lenBuf) {
|
|
21
|
-
const { done, length, numLengthBytes } = decode(byte);
|
|
22
|
-
if (done) {
|
|
23
|
-
return { length, numLengthBytes };
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
throw Error("length decoding failed");
|
|
27
|
-
}
|
|
28
|
-
export function getLengthDecoder() {
|
|
29
|
-
let numLengthBytes = 1;
|
|
30
|
-
let length = 0;
|
|
31
|
-
let multiplier = 1;
|
|
32
|
-
return function addLengthByte(encodedByte) {
|
|
33
|
-
length += (encodedByte & 0x7f) * multiplier;
|
|
34
|
-
multiplier *= 0x80;
|
|
35
|
-
if ((encodedByte & 0x80) === 0) {
|
|
36
|
-
return { done: true, length, numLengthBytes };
|
|
37
|
-
}
|
|
38
|
-
if (numLengthBytes++ >= 4) {
|
|
39
|
-
throw Error("length decoding failed");
|
|
40
|
-
}
|
|
41
|
-
return { done: false, length, numLengthBytes };
|
|
42
|
-
};
|
|
43
|
-
}
|
package/dist/mqttPacket/mod.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
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
|
-
import { PacketNameByType, PacketType } from "./PacketType.js";
|
|
7
|
-
import { invalidTopic, invalidTopicFilter, invalidUTF8 } from "./validators.js";
|
|
8
|
-
import { decodeLength, encodeLength } from "./length.js";
|
|
9
|
-
import { connect } from "./connect.js";
|
|
10
|
-
import { connack } from "./connack.js";
|
|
11
|
-
import { AuthenticationResult, AuthenticationResultByNumber, } from "./AuthenticationResult.js";
|
|
12
|
-
import { publish } from "./publish.js";
|
|
13
|
-
import { puback } from "./puback.js";
|
|
14
|
-
import { pubrec } from "./pubrec.js";
|
|
15
|
-
import { pubrel } from "./pubrel.js";
|
|
16
|
-
import { pubcomp } from "./pubcomp.js";
|
|
17
|
-
import { subscribe } from "./subscribe.js";
|
|
18
|
-
import { suback } from "./suback.js";
|
|
19
|
-
import { unsubscribe } from "./unsubscribe.js";
|
|
20
|
-
import { unsuback } from "./unsuback.js";
|
|
21
|
-
import { pingreq } from "./pingreq.js";
|
|
22
|
-
import { pingres } from "./pingres.js";
|
|
23
|
-
import { disconnect } from "./disconnect.js";
|
|
24
|
-
import { DecoderError } from "./decoder.js";
|
|
25
|
-
export { AuthenticationResult, AuthenticationResultByNumber, decodeLength, encodeLength, invalidTopic, invalidTopicFilter, invalidUTF8, PacketNameByType, PacketType, };
|
|
26
|
-
export const packetsByType = [
|
|
27
|
-
null,
|
|
28
|
-
connect, // 1
|
|
29
|
-
connack, // 2
|
|
30
|
-
publish, // 3
|
|
31
|
-
puback, // 4
|
|
32
|
-
pubrec, // 5
|
|
33
|
-
pubrel, // 6
|
|
34
|
-
pubcomp, // 7
|
|
35
|
-
subscribe, // 8
|
|
36
|
-
suback, // 9
|
|
37
|
-
unsubscribe, // 10
|
|
38
|
-
unsuback, // 11
|
|
39
|
-
pingreq, // 12
|
|
40
|
-
pingres, // 13
|
|
41
|
-
disconnect, // 14
|
|
42
|
-
];
|
|
43
|
-
export function encode(packet) {
|
|
44
|
-
const packetType = packet.type;
|
|
45
|
-
// deno-lint-ignore no-explicit-any
|
|
46
|
-
const pkt = packet;
|
|
47
|
-
const encoded = packetsByType[packetType]?.encode(pkt);
|
|
48
|
-
if (!encoded) {
|
|
49
|
-
throw Error("Packet encoding failed");
|
|
50
|
-
}
|
|
51
|
-
const { flags, bytes } = encoded;
|
|
52
|
-
return Uint8Array.from([
|
|
53
|
-
(packetType << 4) | flags,
|
|
54
|
-
...encodeLength(bytes.length),
|
|
55
|
-
...bytes,
|
|
56
|
-
]);
|
|
57
|
-
}
|
|
58
|
-
export function decodePayload(firstByte, buffer) {
|
|
59
|
-
const packetType = firstByte >> 4;
|
|
60
|
-
const flags = firstByte & 0x0f;
|
|
61
|
-
const packet = packetsByType[packetType]?.decode(buffer, flags);
|
|
62
|
-
if (packet !== undefined) {
|
|
63
|
-
return packet;
|
|
64
|
-
}
|
|
65
|
-
throw new Error("packet decoding failed");
|
|
66
|
-
}
|
|
67
|
-
export function decode(buffer) {
|
|
68
|
-
if (buffer.length < 2) {
|
|
69
|
-
throw new DecoderError("Packet decoding failed");
|
|
70
|
-
}
|
|
71
|
-
const { length, numLengthBytes } = decodeLength(buffer, 1);
|
|
72
|
-
const start = numLengthBytes + 1;
|
|
73
|
-
const end = start + length;
|
|
74
|
-
return decodePayload(buffer[0], buffer.subarray(start, end));
|
|
75
|
-
}
|
|
76
|
-
export { getLengthDecoder } from "../mqttPacket/length.js";
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { PacketType } from "./PacketType.js";
|
|
2
|
-
import { hasEmptyFlags, isEmptyBuf } from "./decoder.js";
|
|
3
|
-
export const pingreq = {
|
|
4
|
-
encode(_packet) {
|
|
5
|
-
const flags = 0;
|
|
6
|
-
return { flags, bytes: [] };
|
|
7
|
-
},
|
|
8
|
-
decode(buffer, flags) {
|
|
9
|
-
hasEmptyFlags(flags);
|
|
10
|
-
isEmptyBuf(buffer);
|
|
11
|
-
return {
|
|
12
|
-
type: PacketType.pingreq,
|
|
13
|
-
};
|
|
14
|
-
},
|
|
15
|
-
};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { PacketType } from "./PacketType.js";
|
|
2
|
-
import { isEmptyBuf } from "./decoder.js";
|
|
3
|
-
export const pingres = {
|
|
4
|
-
encode(_packet) {
|
|
5
|
-
const flags = 0;
|
|
6
|
-
return { flags, bytes: [] };
|
|
7
|
-
},
|
|
8
|
-
decode(buffer) {
|
|
9
|
-
isEmptyBuf(buffer);
|
|
10
|
-
return {
|
|
11
|
-
type: PacketType.pingres,
|
|
12
|
-
};
|
|
13
|
-
},
|
|
14
|
-
};
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { PacketType } from "./PacketType.js";
|
|
2
|
-
import { Decoder } from "./decoder.js";
|
|
3
|
-
import { Encoder } from "./encoder.js";
|
|
4
|
-
export const puback = {
|
|
5
|
-
encode(packet) {
|
|
6
|
-
const flags = 0;
|
|
7
|
-
const encoder = new Encoder();
|
|
8
|
-
encoder.setInt16(packet.id);
|
|
9
|
-
return { flags, bytes: encoder.done() };
|
|
10
|
-
},
|
|
11
|
-
decode(buffer) {
|
|
12
|
-
const decoder = new Decoder(buffer);
|
|
13
|
-
const id = decoder.getInt16();
|
|
14
|
-
decoder.done();
|
|
15
|
-
return {
|
|
16
|
-
type: PacketType.puback,
|
|
17
|
-
id,
|
|
18
|
-
};
|
|
19
|
-
},
|
|
20
|
-
};
|