@seriousme/opifex 1.2.1 → 1.3.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.
Files changed (104) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +72 -4
  3. package/bin/conformanceServer.ts +1 -1
  4. package/bin/mqtt.ts +5 -2
  5. package/client/client.ts +3 -3
  6. package/client/context.ts +2 -0
  7. package/client/mod.ts +1 -2
  8. package/deno/client.ts +4 -3
  9. package/deno/server.ts +3 -2
  10. package/deno.json +4 -1
  11. package/dev_utils/dummyConn.ts +4 -4
  12. package/dist/bin/conformanceServer.js +52 -0
  13. package/dist/bin/demoServer.js +29 -57
  14. package/dist/bin/mqtt.js +148 -187
  15. package/dist/client/ConnectionState.js +7 -0
  16. package/dist/client/client.js +125 -0
  17. package/dist/client/context.js +148 -0
  18. package/dist/client/deps.js +5 -0
  19. package/dist/client/handlers/handleConnack.js +18 -0
  20. package/dist/client/handlers/handlePacket.js +50 -0
  21. package/dist/client/handlers/handlePuback.js +6 -0
  22. package/dist/client/handlers/handlePubcomp.js +9 -0
  23. package/dist/client/handlers/handlePublish.js +28 -0
  24. package/dist/client/handlers/handlePubrec.js +17 -0
  25. package/dist/client/handlers/handlePubrel.js +19 -0
  26. package/dist/client/handlers/handleSuback.js +9 -0
  27. package/dist/client/handlers/handleUnsuback.js +7 -0
  28. package/dist/client/mod.js +5 -3
  29. package/dist/client/store/deps.js +1 -0
  30. package/dist/client/store/memoryStore.js +36 -0
  31. package/dist/client/store/store.js +1 -0
  32. package/dist/mqttConn/mqttConn.js +104 -0
  33. package/dist/mqttPacket/AuthenticationResult.js +12 -0
  34. package/dist/mqttPacket/BitMask.js +10 -0
  35. package/dist/mqttPacket/PacketType.js +18 -0
  36. package/dist/mqttPacket/connack.js +27 -0
  37. package/dist/mqttPacket/connect.js +115 -0
  38. package/dist/mqttPacket/decoder.js +89 -0
  39. package/dist/mqttPacket/disconnect.js +14 -0
  40. package/dist/mqttPacket/encoder.js +56 -0
  41. package/dist/mqttPacket/length.js +43 -0
  42. package/dist/mqttPacket/mod.js +71 -0
  43. package/dist/mqttPacket/pingreq.js +15 -0
  44. package/dist/mqttPacket/pingres.js +14 -0
  45. package/dist/mqttPacket/puback.js +20 -0
  46. package/dist/mqttPacket/pubcomp.js +20 -0
  47. package/dist/mqttPacket/publish.js +51 -0
  48. package/dist/mqttPacket/pubrec.js +20 -0
  49. package/dist/mqttPacket/pubrel.js +20 -0
  50. package/dist/mqttPacket/suback.js +29 -0
  51. package/dist/mqttPacket/subscribe.js +52 -0
  52. package/dist/mqttPacket/types.js +1 -0
  53. package/dist/mqttPacket/unsuback.js +20 -0
  54. package/dist/mqttPacket/unsubscribe.js +36 -0
  55. package/dist/mqttPacket/validators.js +15 -0
  56. package/dist/node/client.js +65 -0
  57. package/dist/node/server.js +22 -0
  58. package/dist/node/wrapNodeSocket.js +47 -0
  59. package/dist/persistence/deps.js +3 -0
  60. package/dist/persistence/memory/memoryPersistence.js +117 -0
  61. package/dist/persistence/persistence.js +1 -0
  62. package/dist/persistence/store.js +1 -0
  63. package/dist/server/context.js +100 -0
  64. package/dist/server/deps.js +5 -0
  65. package/dist/server/handlers/handleConnect.js +46 -0
  66. package/dist/server/handlers/handleDisconnect.js +4 -0
  67. package/dist/server/handlers/handlePacket.js +58 -0
  68. package/dist/server/handlers/handlePingreq.js +6 -0
  69. package/dist/server/handlers/handlePuback.js +8 -0
  70. package/dist/server/handlers/handlePubcomp.js +8 -0
  71. package/dist/server/handlers/handlePublish.js +40 -0
  72. package/dist/server/handlers/handlePubrec.js +15 -0
  73. package/dist/server/handlers/handlePubrel.js +18 -0
  74. package/dist/server/handlers/handleSubscribe.js +33 -0
  75. package/dist/server/handlers/handleUnsubscribe.js +12 -0
  76. package/dist/server/mod.js +7 -589
  77. package/dist/server/server.js +47 -0
  78. package/dist/socket/socket.js +36 -0
  79. package/dist/timer/timer.js +40 -0
  80. package/dist/trie/trie.js +95 -0
  81. package/dist/tsconfig.tsbuildinfo +1 -0
  82. package/dist/utils/args.js +61 -0
  83. package/dist/utils/assert.js +19 -0
  84. package/dist/utils/asyncQueue.js +56 -0
  85. package/dist/utils/deferred.js +11 -0
  86. package/dist/utils/logger.js +29 -0
  87. package/dist/utils/mod.js +7 -0
  88. package/dist/utils/nextTick.js +3 -0
  89. package/dist/utils/platform.js +4 -0
  90. package/mqttConn/mqttConn.ts +4 -1
  91. package/node/client.ts +30 -6
  92. package/node/server.ts +2 -0
  93. package/node/wrapNodeSocket.ts +1 -1
  94. package/package.json +16 -3
  95. package/server/mod.ts +1 -2
  96. package/socket/socket.ts +3 -2
  97. package/timer/timer.ts +1 -1
  98. package/tsconfig.json +3 -0
  99. package/.vscode/extensions.json +0 -10
  100. package/.vscode/settings.json +0 -12
  101. package/deno/wrapDenoConn.ts +0 -12
  102. package/dist/client-DqLTBVC2.js +0 -541
  103. package/dist/timer-DDWVNsyG.js +0 -924
  104. package/dist/wrapNodeSocket-BAJm059T.js +0 -115
@@ -0,0 +1,43 @@
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
+ }
@@ -0,0 +1,71 @@
1
+ import { PacketNameByType, PacketType } from "./PacketType.js";
2
+ import { invalidTopic, invalidTopicFilter, invalidUTF8 } from "./validators.js";
3
+ import { decodeLength, encodeLength } from "./length.js";
4
+ import connect from "./connect.js";
5
+ import connack from "./connack.js";
6
+ import { AuthenticationResult, AuthenticationResultByNumber, } from "./AuthenticationResult.js";
7
+ import publish from "./publish.js";
8
+ import puback from "./puback.js";
9
+ import pubrec from "./pubrec.js";
10
+ import pubrel from "./pubrel.js";
11
+ import pubcomp from "./pubcomp.js";
12
+ import subscribe from "./subscribe.js";
13
+ import suback from "./suback.js";
14
+ import unsubscribe from "./unsubscribe.js";
15
+ import unsuback from "./unsuback.js";
16
+ import pingreq from "./pingreq.js";
17
+ import pingres from "./pingres.js";
18
+ import disconnect from "./disconnect.js";
19
+ import { DecoderError } from "./decoder.js";
20
+ export { AuthenticationResult, AuthenticationResultByNumber, decodeLength, encodeLength, invalidTopic, invalidTopicFilter, invalidUTF8, PacketNameByType, PacketType, };
21
+ export const packetsByType = [
22
+ null,
23
+ connect, // 1
24
+ connack, // 2
25
+ publish, // 3
26
+ puback, // 4
27
+ pubrec, // 5
28
+ pubrel, // 6
29
+ pubcomp, // 7
30
+ subscribe, // 8
31
+ suback, // 9
32
+ unsubscribe, // 10
33
+ unsuback, // 11
34
+ pingreq, // 12
35
+ pingres, // 13
36
+ disconnect, // 14
37
+ ];
38
+ export function encode(packet) {
39
+ const packetType = packet.type;
40
+ // deno-lint-ignore no-explicit-any
41
+ const pkt = packet;
42
+ const encoded = packetsByType[packetType]?.encode(pkt);
43
+ if (!encoded) {
44
+ throw Error("Packet encoding failed");
45
+ }
46
+ const { flags, bytes } = encoded;
47
+ return Uint8Array.from([
48
+ (packetType << 4) | flags,
49
+ ...encodeLength(bytes.length),
50
+ ...bytes,
51
+ ]);
52
+ }
53
+ export function decodePayload(firstByte, buffer) {
54
+ const packetType = firstByte >> 4;
55
+ const flags = firstByte & 0x0f;
56
+ const packet = packetsByType[packetType]?.decode(buffer, flags);
57
+ if (packet !== undefined) {
58
+ return packet;
59
+ }
60
+ throw new Error("packet decoding failed");
61
+ }
62
+ export function decode(buffer) {
63
+ if (buffer.length < 2) {
64
+ throw new DecoderError("Packet decoding failed");
65
+ }
66
+ const { length, numLengthBytes } = decodeLength(buffer, 1);
67
+ const start = numLengthBytes + 1;
68
+ const end = start + length;
69
+ return decodePayload(buffer[0], buffer.subarray(start, end));
70
+ }
71
+ export { getLengthDecoder } from "../mqttPacket/length.js";
@@ -0,0 +1,15 @@
1
+ import { PacketType } from "./PacketType.js";
2
+ import { hasEmptyFlags, isEmptyBuf } from "./decoder.js";
3
+ export default {
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
+ };
@@ -0,0 +1,14 @@
1
+ import { PacketType } from "./PacketType.js";
2
+ import { isEmptyBuf } from "./decoder.js";
3
+ export default {
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
+ };
@@ -0,0 +1,20 @@
1
+ import { PacketType } from "./PacketType.js";
2
+ import { Decoder } from "./decoder.js";
3
+ import { Encoder } from "./encoder.js";
4
+ export default {
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
+ };
@@ -0,0 +1,20 @@
1
+ import { PacketType } from "./PacketType.js";
2
+ import { Decoder } from "./decoder.js";
3
+ import { Encoder } from "./encoder.js";
4
+ export default {
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.pubcomp,
17
+ id,
18
+ };
19
+ },
20
+ };
@@ -0,0 +1,51 @@
1
+ import { PacketType } from "./PacketType.js";
2
+ import { BitMask } from "./BitMask.js";
3
+ import { Encoder, EncoderError } from "./encoder.js";
4
+ import { booleanFlag, Decoder, DecoderError } from "./decoder.js";
5
+ export default {
6
+ encode(packet) {
7
+ const qos = packet.qos || 0;
8
+ const flags = (packet.dup ? BitMask.bit3 : 0) +
9
+ (qos & 2 ? BitMask.bit2 : 0) +
10
+ (qos & 1 ? BitMask.bit1 : 0) +
11
+ (packet.retain ? BitMask.bit0 : 0);
12
+ const encoder = new Encoder();
13
+ encoder.setTopic(packet.topic);
14
+ if (qos === 1 || qos === 2) {
15
+ if (typeof packet.id !== "number" || packet.id < 1) {
16
+ throw new EncoderError("when qos is 1 or 2, packet must have id");
17
+ }
18
+ encoder.setInt16(packet.id);
19
+ }
20
+ encoder.setRemainder(packet.payload);
21
+ return { flags, bytes: encoder.done() };
22
+ },
23
+ decode(buffer, flags) {
24
+ const dup = booleanFlag(flags, BitMask.bit3);
25
+ const qos = (flags & 6) >> 1;
26
+ const retain = booleanFlag(flags, BitMask.bit0);
27
+ if (qos !== 0 && qos !== 1 && qos !== 2) {
28
+ throw new DecoderError("Invalid qos");
29
+ }
30
+ if (dup && qos === 0) {
31
+ throw new DecoderError("Invalid qos for possible duplicate");
32
+ }
33
+ const decoder = new Decoder(buffer);
34
+ const topic = decoder.getTopic();
35
+ let id = 0;
36
+ if (qos > 0) {
37
+ id = decoder.getInt16();
38
+ }
39
+ const payload = decoder.getRemainder();
40
+ // no decoder.done() required because of getRemainder
41
+ return {
42
+ type: PacketType.publish,
43
+ topic,
44
+ payload,
45
+ dup,
46
+ retain,
47
+ qos,
48
+ id,
49
+ };
50
+ },
51
+ };
@@ -0,0 +1,20 @@
1
+ import { PacketType } from "./PacketType.js";
2
+ import { Decoder } from "./decoder.js";
3
+ import { Encoder } from "./encoder.js";
4
+ export default {
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.pubrec,
17
+ id,
18
+ };
19
+ },
20
+ };
@@ -0,0 +1,20 @@
1
+ import { PacketType } from "./PacketType.js";
2
+ import { Decoder } from "./decoder.js";
3
+ import { Encoder } from "./encoder.js";
4
+ export default {
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.pubrel,
17
+ id,
18
+ };
19
+ },
20
+ };
@@ -0,0 +1,29 @@
1
+ import { PacketType } from "./PacketType.js";
2
+ import { Encoder } from "./encoder.js";
3
+ import { Decoder } from "./decoder.js";
4
+ export default {
5
+ encode(packet) {
6
+ const flags = 0;
7
+ const encoder = new Encoder();
8
+ encoder.setInt16(packet.id);
9
+ encoder.setRemainder(packet.returnCodes);
10
+ return {
11
+ flags,
12
+ bytes: encoder.done(),
13
+ };
14
+ },
15
+ decode(buffer) {
16
+ const decoder = new Decoder(buffer);
17
+ const id = decoder.getInt16();
18
+ const payload = decoder.getRemainder();
19
+ const returnCodes = [];
20
+ for (const code of payload) {
21
+ returnCodes.push(code);
22
+ }
23
+ return {
24
+ type: PacketType.suback,
25
+ id,
26
+ returnCodes,
27
+ };
28
+ },
29
+ };
@@ -0,0 +1,52 @@
1
+ import { PacketType } from "./PacketType.js";
2
+ import { BitMask } from "./BitMask.js";
3
+ import { Encoder } from "./encoder.js";
4
+ import { booleanFlag, Decoder, DecoderError } from "./decoder.js";
5
+ export default {
6
+ encode(packet) {
7
+ // Bits 3,2,1 and 0 of the fixed header of the SUBSCRIBE Control Packet are reserved and
8
+ // MUST be set to 0,0,1 and 0 respectively. The Server MUST treat any other value as
9
+ // malformed and close the Network Connection [MQTT-3.8.1-1].
10
+ const flags = 0b0010;
11
+ const encoder = new Encoder();
12
+ encoder.setInt16(packet.id);
13
+ for (const sub of packet.subscriptions) {
14
+ encoder.setTopic(sub.topicFilter);
15
+ encoder.setByte(sub.qos);
16
+ }
17
+ return { flags, bytes: encoder.done() };
18
+ },
19
+ decode(buffer, flags) {
20
+ // Bits 3,2,1 and 0 of the fixed header of the SUBSCRIBE Control Packet are reserved and
21
+ // MUST be set to 0,0,1 and 0 respectively. The Server MUST treat any other value as
22
+ // malformed and close the Network Connection [MQTT-3.8.1-1].
23
+ if (!booleanFlag(flags, BitMask.bit1)) {
24
+ throw new DecoderError("Invalid header");
25
+ }
26
+ const decoder = new Decoder(buffer);
27
+ const id = decoder.getInt16();
28
+ const subscriptions = [];
29
+ // The payload of a SUBSCRIBE packet MUST contain at least one Topic Filter / QoS pair.
30
+ // A SUBSCRIBE packet with no payload is a protocol violation [MQTT-3.8.3-3].
31
+ do {
32
+ const topicFilter = decoder.getTopicFilter();
33
+ const qos = decoder.getByte();
34
+ if (qos !== 0 && qos !== 1 && qos !== 2) {
35
+ throw new DecoderError("Invalid qos");
36
+ }
37
+ if (qos > 0 && id === 0) {
38
+ throw new DecoderError("Invalid packet identifier");
39
+ }
40
+ subscriptions.push({
41
+ topicFilter: topicFilter,
42
+ qos,
43
+ });
44
+ } while (!decoder.atEnd());
45
+ decoder.done();
46
+ return {
47
+ type: PacketType.subscribe,
48
+ id,
49
+ subscriptions,
50
+ };
51
+ },
52
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ import { PacketType } from "./PacketType.js";
2
+ import { Decoder } from "./decoder.js";
3
+ import { Encoder } from "./encoder.js";
4
+ export default {
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.unsuback,
17
+ id,
18
+ };
19
+ },
20
+ };
@@ -0,0 +1,36 @@
1
+ import { PacketType } from "./PacketType.js";
2
+ import { BitMask } from "./BitMask.js";
3
+ import { Encoder } from "./encoder.js";
4
+ import { booleanFlag, Decoder, DecoderError } from "./decoder.js";
5
+ export default {
6
+ encode(packet) {
7
+ // Bits 3,2,1 and 0 of the fixed header of the UNSUBSCRIBE Control Packet are reserved and
8
+ // MUST be set to 0,0,1 and 0 respectively. The Server MUST treat any other value as
9
+ // malformed and close the Network Connection [MQTT-3.10.1-1].
10
+ const flags = 0b0010;
11
+ const encoder = new Encoder();
12
+ encoder.setInt16(packet.id);
13
+ for (const topicFilter of packet.topicFilters) {
14
+ encoder.setTopicFilter(topicFilter);
15
+ }
16
+ return { flags, bytes: encoder.done() };
17
+ },
18
+ decode(buffer, flags) {
19
+ if (!booleanFlag(flags, BitMask.bit1)) {
20
+ throw new DecoderError("Invalid header");
21
+ }
22
+ const decoder = new Decoder(buffer);
23
+ const id = decoder.getInt16();
24
+ const topicFilters = [];
25
+ do {
26
+ const topicFilter = decoder.getTopicFilter();
27
+ topicFilters.push(topicFilter);
28
+ } while (!decoder.atEnd());
29
+ decoder.done();
30
+ return {
31
+ type: PacketType.unsubscribe,
32
+ id,
33
+ topicFilters,
34
+ };
35
+ },
36
+ };
@@ -0,0 +1,15 @@
1
+ // deno-lint-ignore no-control-regex
2
+ const invalidUTF8regEx = new RegExp(/\x00|\uFFFD/);
3
+ // deno-lint-ignore no-control-regex
4
+ const invalidTopicRegEx = new RegExp(/^$|\+|#|\x00|\uFFFD/);
5
+ // deno-lint-ignore no-control-regex
6
+ const invalidTopicFilterRegEx = new RegExp(/^$|#.|[^\/]\+|\+[^\/]|\x00|\uFFFD/);
7
+ export function invalidUTF8(value) {
8
+ return invalidUTF8regEx.test(value);
9
+ }
10
+ export function invalidTopic(value) {
11
+ return invalidTopicRegEx.test(value);
12
+ }
13
+ export function invalidTopicFilter(value) {
14
+ return invalidTopicFilterRegEx.test(value);
15
+ }
@@ -0,0 +1,65 @@
1
+ // this a NodeJS specific implementation of TCP client sockets
2
+ // it extends the platform agnostic Client class
3
+ import { Client } from "../client/client.js";
4
+ import { logger } from "../client/deps.js";
5
+ import { readFile } from "node:fs/promises";
6
+ import { createConnection } from "node:net";
7
+ import * as tls from "node:tls";
8
+ import { wrapNodeSocket } from "./wrapNodeSocket.js";
9
+ export async function getCaCerts(filename) {
10
+ if (!filename) {
11
+ return;
12
+ }
13
+ const caCerts = await readFile(filename, { encoding: "utf-8" });
14
+ if (caCerts === "") {
15
+ return;
16
+ }
17
+ return [caCerts];
18
+ }
19
+ export class TcpClient extends Client {
20
+ connectMQTT(hostname, port = 1883) {
21
+ logger.debug({ hostname, port });
22
+ return new Promise((resolve, reject) => {
23
+ const socket = createConnection({ port, host: hostname }, () => {
24
+ logger.debug("Connected to server");
25
+ resolve(wrapNodeSocket(socket));
26
+ });
27
+ socket.once("error", (err) => {
28
+ // @ts-ignore the type spec of err is missing err.code
29
+ logger.debug("Connection failed: ", err);
30
+ reject(err);
31
+ });
32
+ });
33
+ }
34
+ connectMQTTS(hostname, port = 8883, caCerts) {
35
+ const opts = {
36
+ host: hostname,
37
+ port,
38
+ secureContext: caCerts
39
+ ? tls.createSecureContext({ cert: caCerts })
40
+ : undefined,
41
+ };
42
+ logger.debug({ hostname, port, caCerts });
43
+ return new Promise((resolve, reject) => {
44
+ const socket = tls.connect(opts, () => {
45
+ logger.debug("Connected to server");
46
+ resolve(wrapNodeSocket(socket));
47
+ });
48
+ socket.once("error", (err) => {
49
+ logger.debug("Connection failed", err);
50
+ reject(err);
51
+ });
52
+ });
53
+ }
54
+ createConn(protocol, hostname, port, caCerts) {
55
+ // if you need to support alternative connection types just
56
+ // overload this method in your subclass
57
+ if (protocol === "mqtts:") {
58
+ return this.connectMQTTS(hostname, port, caCerts);
59
+ }
60
+ if (protocol === "mqtt:") {
61
+ return this.connectMQTT(hostname, port);
62
+ }
63
+ throw `Unsupported protocol: ${protocol}`;
64
+ }
65
+ }
@@ -0,0 +1,22 @@
1
+ import { createServer } from "node:net";
2
+ import { MqttServer } from "../server/mod.js";
3
+ import { wrapNodeSocket } from "./wrapNodeSocket.js";
4
+ export class TcpServer {
5
+ mqttServer;
6
+ server;
7
+ serverOptions;
8
+ constructor(serverOptions, mqttOptions) {
9
+ this.mqttServer = new MqttServer(mqttOptions);
10
+ this.serverOptions = serverOptions;
11
+ }
12
+ start() {
13
+ this.server = createServer((sock) => this.mqttServer.serve(wrapNodeSocket(sock)));
14
+ this.server.listen(this.serverOptions.port, this.serverOptions.hostname);
15
+ }
16
+ stop() {
17
+ this.server?.close();
18
+ }
19
+ get port() {
20
+ return this.serverOptions.port;
21
+ }
22
+ }
@@ -0,0 +1,47 @@
1
+ import { Writable } from "node:stream";
2
+ function closer(sock) {
3
+ if (!sock.closed) {
4
+ sock.end();
5
+ }
6
+ }
7
+ export function wrapNodeSocket(socket) {
8
+ const readable = new ReadableStream({
9
+ type: "bytes",
10
+ start(controller) {
11
+ socket.on("data", (data) => {
12
+ controller.enqueue(data);
13
+ const desiredSize = controller.desiredSize ?? 0;
14
+ if (desiredSize <= 0) {
15
+ // The internal queue is full, so propagate
16
+ // the backpressure signal to the underlying source.
17
+ socket.pause();
18
+ }
19
+ });
20
+ socket.on("error", (err) => controller.error(err));
21
+ socket.on("end", () => {
22
+ // close the controller
23
+ controller.close();
24
+ // and unlock the last BYOB read request
25
+ controller.byobRequest?.respond(0);
26
+ });
27
+ },
28
+ pull: () => {
29
+ socket.resume();
30
+ },
31
+ cancel: () => {
32
+ socket.end();
33
+ },
34
+ });
35
+ const writable = Writable.toWeb(socket);
36
+ const remoteAddr = {
37
+ hostname: socket.remoteAddress || "",
38
+ port: socket.remotePort || 0,
39
+ };
40
+ const conn = {
41
+ readable: readable,
42
+ writable: writable,
43
+ close: () => closer(socket),
44
+ remoteAddr,
45
+ };
46
+ return conn;
47
+ }
@@ -0,0 +1,3 @@
1
+ export { PacketType } from "../mqttPacket/mod.js";
2
+ export { Trie } from "../trie/trie.js";
3
+ export { logger } from "../utils/mod.js";