@seriousme/opifex 1.2.1 → 1.4.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 (108) hide show
  1. package/.github/workflows/update-license-year.sh +14 -0
  2. package/.github/workflows/update-license-year.yml +13 -0
  3. package/CHANGELOG.md +16 -0
  4. package/LICENSE.txt +1 -1
  5. package/README.md +72 -4
  6. package/bin/conformanceServer.ts +1 -1
  7. package/bin/mqtt.ts +5 -2
  8. package/client/client.ts +13 -3
  9. package/client/context.ts +2 -0
  10. package/client/mod.ts +1 -2
  11. package/deno/client.ts +10 -5
  12. package/deno/server.ts +3 -2
  13. package/deno.json +4 -1
  14. package/deno.lock +11 -8
  15. package/dev_utils/dummyConn.ts +4 -4
  16. package/dist/bin/conformanceServer.js +52 -0
  17. package/dist/bin/demoServer.js +29 -57
  18. package/dist/bin/mqtt.js +148 -187
  19. package/dist/client/ConnectionState.js +7 -0
  20. package/dist/client/client.js +129 -0
  21. package/dist/client/context.js +148 -0
  22. package/dist/client/deps.js +5 -0
  23. package/dist/client/handlers/handleConnack.js +18 -0
  24. package/dist/client/handlers/handlePacket.js +50 -0
  25. package/dist/client/handlers/handlePuback.js +6 -0
  26. package/dist/client/handlers/handlePubcomp.js +9 -0
  27. package/dist/client/handlers/handlePublish.js +28 -0
  28. package/dist/client/handlers/handlePubrec.js +17 -0
  29. package/dist/client/handlers/handlePubrel.js +19 -0
  30. package/dist/client/handlers/handleSuback.js +9 -0
  31. package/dist/client/handlers/handleUnsuback.js +7 -0
  32. package/dist/client/mod.js +5 -3
  33. package/dist/client/store/deps.js +1 -0
  34. package/dist/client/store/memoryStore.js +36 -0
  35. package/dist/client/store/store.js +1 -0
  36. package/dist/mqttConn/mqttConn.js +104 -0
  37. package/dist/mqttPacket/AuthenticationResult.js +12 -0
  38. package/dist/mqttPacket/BitMask.js +10 -0
  39. package/dist/mqttPacket/PacketType.js +18 -0
  40. package/dist/mqttPacket/connack.js +27 -0
  41. package/dist/mqttPacket/connect.js +115 -0
  42. package/dist/mqttPacket/decoder.js +89 -0
  43. package/dist/mqttPacket/disconnect.js +14 -0
  44. package/dist/mqttPacket/encoder.js +56 -0
  45. package/dist/mqttPacket/length.js +43 -0
  46. package/dist/mqttPacket/mod.js +71 -0
  47. package/dist/mqttPacket/pingreq.js +15 -0
  48. package/dist/mqttPacket/pingres.js +14 -0
  49. package/dist/mqttPacket/puback.js +20 -0
  50. package/dist/mqttPacket/pubcomp.js +20 -0
  51. package/dist/mqttPacket/publish.js +51 -0
  52. package/dist/mqttPacket/pubrec.js +20 -0
  53. package/dist/mqttPacket/pubrel.js +20 -0
  54. package/dist/mqttPacket/suback.js +29 -0
  55. package/dist/mqttPacket/subscribe.js +52 -0
  56. package/dist/mqttPacket/types.js +1 -0
  57. package/dist/mqttPacket/unsuback.js +20 -0
  58. package/dist/mqttPacket/unsubscribe.js +36 -0
  59. package/dist/mqttPacket/validators.js +15 -0
  60. package/dist/node/client.js +64 -0
  61. package/dist/node/server.js +22 -0
  62. package/dist/node/wrapNodeSocket.js +47 -0
  63. package/dist/persistence/deps.js +3 -0
  64. package/dist/persistence/memory/memoryPersistence.js +117 -0
  65. package/dist/persistence/persistence.js +1 -0
  66. package/dist/persistence/store.js +1 -0
  67. package/dist/server/context.js +100 -0
  68. package/dist/server/deps.js +5 -0
  69. package/dist/server/handlers/handleConnect.js +46 -0
  70. package/dist/server/handlers/handleDisconnect.js +4 -0
  71. package/dist/server/handlers/handlePacket.js +58 -0
  72. package/dist/server/handlers/handlePingreq.js +6 -0
  73. package/dist/server/handlers/handlePuback.js +8 -0
  74. package/dist/server/handlers/handlePubcomp.js +8 -0
  75. package/dist/server/handlers/handlePublish.js +40 -0
  76. package/dist/server/handlers/handlePubrec.js +15 -0
  77. package/dist/server/handlers/handlePubrel.js +18 -0
  78. package/dist/server/handlers/handleSubscribe.js +33 -0
  79. package/dist/server/handlers/handleUnsubscribe.js +12 -0
  80. package/dist/server/mod.js +7 -589
  81. package/dist/server/server.js +47 -0
  82. package/dist/socket/socket.js +36 -0
  83. package/dist/timer/timer.js +40 -0
  84. package/dist/trie/trie.js +95 -0
  85. package/dist/tsconfig.tsbuildinfo +1 -0
  86. package/dist/utils/args.js +61 -0
  87. package/dist/utils/assert.js +19 -0
  88. package/dist/utils/asyncQueue.js +56 -0
  89. package/dist/utils/deferred.js +11 -0
  90. package/dist/utils/logger.js +29 -0
  91. package/dist/utils/mod.js +7 -0
  92. package/dist/utils/nextTick.js +3 -0
  93. package/dist/utils/platform.js +4 -0
  94. package/mqttConn/mqttConn.ts +4 -1
  95. package/node/client.ts +29 -6
  96. package/node/server.ts +2 -0
  97. package/node/wrapNodeSocket.ts +1 -1
  98. package/package.json +17 -4
  99. package/server/mod.ts +1 -2
  100. package/socket/socket.ts +3 -2
  101. package/timer/timer.ts +1 -1
  102. package/tsconfig.json +3 -0
  103. package/.vscode/extensions.json +0 -10
  104. package/.vscode/settings.json +0 -12
  105. package/deno/wrapDenoConn.ts +0 -12
  106. package/dist/client-DqLTBVC2.js +0 -541
  107. package/dist/timer-DDWVNsyG.js +0 -924
  108. package/dist/wrapNodeSocket-BAJm059T.js +0 -115
@@ -0,0 +1,14 @@
1
+ #!/bin/bash
2
+
3
+ license='LICENSE.txt'
4
+ prefix='2016-'
5
+ current_year=$(date +%Y)
6
+ sed -i -e "s/$prefix\([0-9\]\+\)/$prefix$current_year/" $license
7
+
8
+ if [ "$(git diff $license)" ]; then
9
+ git add $license
10
+ git commit -m "Update license year to $current_year"
11
+ git push
12
+ else
13
+ echo "No changes detected in $license"
14
+ fi
@@ -0,0 +1,13 @@
1
+ name: Update License Year
2
+
3
+ on:
4
+ schedule:
5
+ - cron: "5 12 1 1 *"
6
+
7
+ jobs:
8
+ update_license:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - name: Update license year
13
+ run: ./update_license.sh
package/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@
4
4
 
5
5
  ### Changed
6
6
 
7
+ ## [v1.3.0] 19-11-2024
8
+
9
+ ### Changed
10
+
11
+ - update: added architectural description of Opifex to documentation
12
+ - update: added comments to platform specific code to explain boundary between
13
+ platform agnostic and platform specific.
14
+ - update: set default clientId in client
15
+ - fix: improve handling of errors if client connect fails
16
+
17
+ ## [v1.2.1] 01-11-2024
18
+
19
+ ### Changed
20
+
21
+ - fix: unlock last BYOB request using respond(0)
22
+
7
23
  ## [v1.2.0] 01-11-2024
8
24
 
9
25
  ### Changed
package/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 Hans Klunder
3
+ Copyright (c) 2023-2025 Hans Klunder
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -10,14 +10,82 @@ party dependencies, it only relies on built in modules.
10
10
  Its a work in progress, only does MQTT 3.1.1 and currently only has memory based
11
11
  persistence.
12
12
 
13
- There are separate usage instructions for:
13
+ ## Example
14
+
15
+ A simple server example using Deno, for more elaborate examples see the Usage
16
+ section below.
17
+
18
+ ```typescript
19
+ import { MqttServer } from "./server/mod.ts";
20
+
21
+ const listener = Deno.listen();
22
+ const mqttServer = new MqttServer(mqttOptions);
23
+ for await (const conn of this.listener) {
24
+ mqttServer.serve(conn);
25
+ }
26
+ ```
27
+
28
+ ## Architecture
29
+
30
+ 1. The basis of Opifex is the MQTT packet module
31
+ ([mqttPacket/mod.ts](mqttPacket/mod.ts)) which contains al the logic to
32
+ encode and decode packets.
33
+
34
+ 2. On top of mqttPacket sits the MQTT connection module
35
+ ([mqttConn/mod.ts](mqttConn/mod.ts)) module that reads packets from a
36
+ Readable stream and writes them to Writable stream. It will take care of
37
+ incomplete and/or malformed packets. mqttConn provides an async iterable that
38
+ can be awaited for new packets.
39
+
40
+ 3. On top of mqttConn live the MQTT server ([server/mod.ts](server/mod.ts)) and
41
+ MQTT client ([client/mod.ts](client/mod.ts)) that take care of the MQTT
42
+ protocol handling like requiring an authentication to be successfull before
43
+ another type of packet will be accepted. Both follow a similar model of
44
+ implementation where for each packet that is received a handler is invoked
45
+ which then triggers the next step. Server and client are totally independ of
46
+ the technical implementation of the connection and only need a socketConn
47
+ ([socket](socket)) to be able to work.
48
+
49
+ 4. As the server needs to be able to serve multiple clients at the same time, it
50
+ maintains a context ([server/context.ts](server/context.ts)) per client to
51
+ keep track of its state and associated timers.
52
+
53
+ 5. Persistence of data is handled by a pluggable persistence module
54
+ ([persistence](persistence)) which currently only offers memory persistence
55
+ ([persistence/memory](persistence/memory)) but can be extended with database
56
+ backed persistence supported by third party modules.
57
+
58
+ 6. The demo server listens to a platform specific socket and runs the `serve()`
59
+ method from the server module on the platform independent streams of every
60
+ connection.
61
+
62
+ 7. The demo client opens a platform specific socket and passes the resulting
63
+ platform independent streams to the client module.
64
+
65
+ ## Usage
66
+
67
+ The most easy way to use this project is to just use the demo server
68
+ (demoServer) and/or the demo client (mqtt). There are separate usage
69
+ instructions for:
14
70
 
15
71
  - [Deno](deno/README.md)
16
72
  - [NodeJS](node/README.md)
17
73
 
18
- The client and server libs should also work on Bun as they are engine
19
- independent, but the demo server and client currently do not work on Bun because
20
- of some socket incompatibilities between Bun and the rest.
74
+ If you want to change the behaviour of the server and/or the client beyond what
75
+ can be done with CLI options then the next step is to clone the demo server
76
+ and/or the client scripts and modify them to your liking.
77
+
78
+ If you want to port the platform independent client and server libs to other
79
+ types of transport (e.g. Unix sockets or websocketstream) then its recommended
80
+ to clone and modify the platform specific code in `/node` or `/deno` as well.
81
+
82
+ If you want to port the platform independent client and server libs to another
83
+ platform (e.g. Bun) then the platform specific code in `/node` or `/deno` might
84
+ serve as inspiration.
85
+
86
+ The platform independent client and server libs should also work on Bun as they
87
+ are engine independent, but the demo server and client currently do not work on
88
+ Bun because of some socket incompatibilities between Bun and the rest.
21
89
 
22
90
  ## Naming
23
91
 
@@ -5,7 +5,7 @@ import { AuthenticationResult } from "../server/mod.ts";
5
5
  import type { Context, TAuthenticationResult, Topic } from "../server/mod.ts";
6
6
  import { logger, LogLevel } from "../utils/mod.ts";
7
7
  import { getArgs, parseArgs } from "../utils/mod.ts";
8
- import { TcpServer } from "../deno/server.ts";
8
+ import { TcpServer } from "../node/server.ts";
9
9
 
10
10
  const utf8Decoder = new TextDecoder();
11
11
  const userTable = new Map();
package/bin/mqtt.ts CHANGED
@@ -40,6 +40,7 @@ const connectOpts = {
40
40
  boolean: ["noClean", "help"],
41
41
  default: {
42
42
  noClean: false,
43
+ clientId: `Opifex-${crypto.randomUUID()}`,
43
44
  },
44
45
  };
45
46
 
@@ -120,7 +121,8 @@ async function subscribe() {
120
121
  }
121
122
  } catch (err) {
122
123
  if (err instanceof Error) {
123
- logger.info(err.message);
124
+ // @ts-ignore the type spec of err is missing err.code
125
+ logger.info(`Error: ${err.message || err.code}`);
124
126
  }
125
127
  }
126
128
  }
@@ -188,7 +190,8 @@ async function publish() {
188
190
  logger.debug("Disconnected !");
189
191
  } catch (err) {
190
192
  if (err instanceof Error) {
191
- logger.info(err.message);
193
+ // @ts-ignore the type spec of err is missing err.code
194
+ logger.info(`Error: ${err.message || err.code}`);
192
195
  }
193
196
  }
194
197
  }
package/client/client.ts CHANGED
@@ -25,6 +25,8 @@ type ConnectOptions = Omit<
25
25
  export type ConnectParameters = {
26
26
  url?: URL;
27
27
  caCerts?: string[];
28
+ cert?: string;
29
+ key?: string;
28
30
  numberOfRetries?: number;
29
31
  options?: ConnectOptions;
30
32
  };
@@ -70,6 +72,8 @@ export class Client {
70
72
  protected keepAlive = DEFAULT_KEEPALIVE;
71
73
  protected autoReconnect = true;
72
74
  private caCerts?: string[];
75
+ private cert?: string;
76
+ private key?: string;
73
77
  private clientId: string;
74
78
  private ctx = new Context(new MemoryStore());
75
79
  private connectPacket?: ConnectPacket;
@@ -84,6 +88,8 @@ export class Client {
84
88
  _hostname: string,
85
89
  _port?: number,
86
90
  _caCerts?: string[],
91
+ _cert?: string,
92
+ _key?: string,
87
93
  ): Promise<SockConn> {
88
94
  // if you need to support alternative connection types just
89
95
  // overload this method in your subclass
@@ -96,7 +102,7 @@ export class Client {
96
102
  }
97
103
  let isReconnect = false;
98
104
  let attempt = 1;
99
- let lastMessage = "";
105
+ let lastMessage = new Error();
100
106
  let tryConnect = true;
101
107
  while (tryConnect) {
102
108
  logger.debug(`${isReconnect ? "re" : ""}connecting`);
@@ -106,6 +112,8 @@ export class Client {
106
112
  this.url.hostname,
107
113
  Number(this.url.port) ?? undefined,
108
114
  this.caCerts,
115
+ this.cert,
116
+ this.key,
109
117
  );
110
118
  // if we get this far we have a connection
111
119
  tryConnect =
@@ -117,7 +125,7 @@ export class Client {
117
125
  this.ctx.close();
118
126
  } catch (err) {
119
127
  if (err instanceof Error) {
120
- lastMessage = `Connection failed: ${err.message}`;
128
+ lastMessage = err;
121
129
  }
122
130
  logger.debug(lastMessage);
123
131
  if (!isReconnect && attempt > this.numberOfRetries) {
@@ -129,7 +137,7 @@ export class Client {
129
137
  }
130
138
 
131
139
  if (isReconnect === false) {
132
- this.ctx.unresolvedConnect?.reject(Error(lastMessage));
140
+ this.ctx.unresolvedConnect?.reject(lastMessage);
133
141
  }
134
142
  }
135
143
 
@@ -137,6 +145,8 @@ export class Client {
137
145
  this.url = params?.url || this.url;
138
146
  this.numberOfRetries = params.numberOfRetries || this.numberOfRetries;
139
147
  this.caCerts = params?.caCerts;
148
+ this.cert = params?.cert;
149
+ this.key = params?.key;
140
150
  const options = Object.assign(
141
151
  {
142
152
  keepAlive: this.keepAlive,
package/client/context.ts CHANGED
@@ -91,8 +91,10 @@ export class Context {
91
91
  if (this.mqttConn === undefined) {
92
92
  return true;
93
93
  }
94
+ logger.debug("Send connect packet");
94
95
  await this.connect(connectPacket);
95
96
  try {
97
+ logger.debug("Accepting packets");
96
98
  for await (const packet of this.mqttConn) {
97
99
  handlePacket(this, packet);
98
100
  }
package/client/mod.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  /**
2
- * This module provides an MQTT Client interface
3
- * see the /bin folder for examples
2
+ * This module provides an MQTT Client interface see the /bin folder for examples
4
3
  * @module
5
4
  */
6
5
  export { Client, DEFAULT_URL } from "./client.ts";
package/deno/client.ts CHANGED
@@ -1,6 +1,7 @@
1
+ // This a Deno specific implementation of TCP client sockets
2
+ // it extends the platform agnostic Client class
1
3
  import { Client } from "../client/client.ts";
2
4
  import { logger } from "../client/deps.ts";
3
- import { wrapDenoConn } from "./wrapDenoConn.ts";
4
5
  import type { SockConn } from "../client/deps.ts";
5
6
 
6
7
  export async function getCaCerts(filename: string | undefined) {
@@ -17,16 +18,18 @@ export async function getCaCerts(filename: string | undefined) {
17
18
  export class TcpClient extends Client {
18
19
  protected async connectMQTT(hostname: string, port = 1883) {
19
20
  logger.debug({ hostname, port });
20
- return wrapDenoConn(await Deno.connect({ hostname, port }));
21
+ return await Deno.connect({ hostname, port });
21
22
  }
22
23
 
23
24
  protected async connectMQTTS(
24
25
  hostname: string,
25
26
  port = 8883,
26
27
  caCerts?: string[],
28
+ cert?: string,
29
+ key?: string,
27
30
  ) {
28
- logger.debug({ hostname, port, caCerts });
29
- return wrapDenoConn(await Deno.connectTls({ hostname, port, caCerts }));
31
+ logger.debug({ hostname, port, caCerts, cert });
32
+ return await Deno.connectTls({ hostname, port, caCerts, cert, key });
30
33
  }
31
34
 
32
35
  protected override createConn(
@@ -34,11 +37,13 @@ export class TcpClient extends Client {
34
37
  hostname: string,
35
38
  port?: number,
36
39
  caCerts?: string[],
40
+ cert?: string,
41
+ key?: string,
37
42
  ): Promise<SockConn> {
38
43
  // if you need to support alternative connection types just
39
44
  // overload this method in your subclass
40
45
  if (protocol === "mqtts:") {
41
- return this.connectMQTTS(hostname, port, caCerts);
46
+ return this.connectMQTTS(hostname, port, caCerts, cert, key);
42
47
  }
43
48
  if (protocol === "mqtt:") {
44
49
  return this.connectMQTT(hostname, port);
package/deno/server.ts CHANGED
@@ -1,6 +1,7 @@
1
+ // This a Deno specific implementation of a TCP socket listener
2
+ // it uses the platform agnostic MqttServer class
1
3
  import { MqttServer } from "../server/mod.ts";
2
4
  import type { MqttServerOptions } from "../server/mod.ts";
3
- import { wrapDenoConn } from "./wrapDenoConn.ts";
4
5
 
5
6
  export class TcpServer {
6
7
  private listener: Deno.Listener<Deno.Conn>;
@@ -15,7 +16,7 @@ export class TcpServer {
15
16
 
16
17
  async start() {
17
18
  for await (const conn of this.listener) {
18
- this.mqttServer.serve(wrapDenoConn(conn));
19
+ this.mqttServer.serve(conn);
19
20
  }
20
21
  }
21
22
  stop() {
package/deno.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@seriousme/opifex",
3
+ "version": "1.4.0",
3
4
  "exports": {
4
5
  "./server": "./server/mod.ts",
5
6
  "./client": "./client/mod.ts"
6
7
  },
7
- "exclude": ["dist"]
8
+ "exclude": [
9
+ "dist"
10
+ ]
8
11
  }
package/deno.lock CHANGED
@@ -2,29 +2,32 @@
2
2
  "version": "4",
3
3
  "specifiers": {
4
4
  "npm:@types/node@*": "22.5.4",
5
- "npm:@types/node@^22.8.6": "22.8.6"
5
+ "npm:@types/node@^22.10.0": "22.10.0"
6
6
  },
7
7
  "npm": {
8
- "@types/node@22.5.4": {
9
- "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
8
+ "@types/node@22.10.0": {
9
+ "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==",
10
10
  "dependencies": [
11
- "undici-types"
11
+ "undici-types@6.20.0"
12
12
  ]
13
13
  },
14
- "@types/node@22.8.6": {
15
- "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==",
14
+ "@types/node@22.5.4": {
15
+ "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
16
16
  "dependencies": [
17
- "undici-types"
17
+ "undici-types@6.19.8"
18
18
  ]
19
19
  },
20
20
  "undici-types@6.19.8": {
21
21
  "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
22
+ },
23
+ "undici-types@6.20.0": {
24
+ "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="
22
25
  }
23
26
  },
24
27
  "workspace": {
25
28
  "packageJson": {
26
29
  "dependencies": [
27
- "npm:@types/node@^22.8.6"
30
+ "npm:@types/node@^22.10.0"
28
31
  ]
29
32
  }
30
33
  }
@@ -68,7 +68,7 @@ function ReadableStreamFrom(
68
68
  export function makeDummySockConn(
69
69
  readerBuffs: Uint8Array[],
70
70
  writerBuf: Uint8Array,
71
- closer = () => {},
71
+ close = () => {},
72
72
  ) {
73
73
  const readBlob = new Blob(readerBuffs);
74
74
  const readable = readBlob.stream();
@@ -76,20 +76,20 @@ export function makeDummySockConn(
76
76
  return {
77
77
  readable,
78
78
  writable,
79
- closer,
79
+ close,
80
80
  };
81
81
  }
82
82
 
83
83
  export function makeDummyQueueSockConn(
84
84
  r: AsyncQueue<Uint8Array>,
85
85
  w: AsyncQueue<Uint8Array>,
86
- closer = () => {},
86
+ close = () => {},
87
87
  ) {
88
88
  const readable = ReadableStreamFrom(r);
89
89
  const writable = new WritableStream(new Uint8QueuedWriter(w));
90
90
  return {
91
91
  readable,
92
92
  writable,
93
- closer,
93
+ close,
94
94
  };
95
95
  }
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env -S node --experimental-strip-types
2
+ // this server is intended to be used with https://github.com/eclipse/iottestware
3
+ import { AuthenticationResult } from "../server/mod.js";
4
+ import { logger, LogLevel } from "../utils/mod.js";
5
+ import { getArgs, parseArgs } from "../utils/mod.js";
6
+ import { TcpServer } from "../node/server.js";
7
+ const utf8Decoder = new TextDecoder();
8
+ const userTable = new Map();
9
+ userTable.set("IoTester_1", "strong_password");
10
+ userTable.set("IoTester_2", "strong_password");
11
+ const strictUsername = new RegExp(/^[a-zA-Z0-9]{0,23}$/);
12
+ const notAuthorizedTable = new Set();
13
+ notAuthorizedTable.add(["123-456-789", "eclipse/iot/tesware/0data"]);
14
+ function isAuthenticated(_ctx, clientId, username, password) {
15
+ const pwd = utf8Decoder.decode(password);
16
+ logger.debug(`Verifying authentication of client '${clientId}' with username '${username}' and password '${pwd}'`);
17
+ if (!userTable.has(username)) {
18
+ if (!strictUsername.test(username)) {
19
+ return AuthenticationResult.badUsernameOrPassword;
20
+ }
21
+ }
22
+ const pass = userTable.get(username);
23
+ if (pwd === pass) {
24
+ return AuthenticationResult.ok;
25
+ }
26
+ return AuthenticationResult.notAuthorized;
27
+ }
28
+ function isAuthorizedToPublish(ctx, topic) {
29
+ logger.debug(`Checking authorization of client '${ctx.store?.clientId}' to publish on topic '${topic}'`);
30
+ // if (notAuthorizedTable.has([ctx.client,topic])){
31
+ // return false;
32
+ // }
33
+ return true;
34
+ }
35
+ function isAuthorizedToSubscribe(ctx, topic) {
36
+ logger.debug(`Checking authorization of client '${ctx.store?.clientId}' to subscribe to topic '${topic}'`);
37
+ return true;
38
+ }
39
+ /** start the server **/
40
+ const { _: [portNum] } = parseArgs(getArgs());
41
+ const port = Number(portNum ?? 1883);
42
+ const hostname = "::";
43
+ logger.level(LogLevel.info);
44
+ const tcpServer = new TcpServer({ port, hostname }, {
45
+ handlers: {
46
+ isAuthenticated,
47
+ isAuthorizedToPublish,
48
+ isAuthorizedToSubscribe,
49
+ },
50
+ });
51
+ tcpServer.start();
52
+ logger.info(`Server started on port ${tcpServer.port}`);
@@ -1,74 +1,46 @@
1
- #!/usr/bin/env node
2
- import { createServer } from "node:net";
3
- import {
4
- A as AuthenticationResult,
5
- L as LogLevel,
6
- l as logger,
7
- } from "../timer-DDWVNsyG.js";
8
- import "node:process";
9
- import { MqttServer } from "../server/mod.js";
10
- import {
11
- g as getArgs,
12
- p as parseArgs,
13
- w as wrapNodeSocket,
14
- } from "../wrapNodeSocket-BAJm059T.js";
15
- import "node:stream";
16
-
17
- class TcpServer {
18
- mqttServer;
19
- server;
20
- serverOptions;
21
- constructor(serverOptions, mqttOptions) {
22
- this.mqttServer = new MqttServer(mqttOptions);
23
- this.serverOptions = serverOptions;
24
- }
25
- start() {
26
- this.server = createServer(
27
- (sock) => this.mqttServer.serve(wrapNodeSocket(sock)),
28
- );
29
- this.server.listen(this.serverOptions.port, this.serverOptions.hostname);
30
- }
31
- stop() {
32
- this.server?.close();
33
- }
34
- get port() {
35
- return this.serverOptions.port;
36
- }
37
- }
38
-
1
+ #!/usr/bin/env -S node --experimental-strip-types
2
+ import { TcpServer } from "../node/server.js";
3
+ import { AuthenticationResult } from "../server/mod.js";
4
+ import { getArgs, logger, LogLevel, parseArgs } from "../utils/mod.js";
39
5
  const utf8Decoder = new TextDecoder();
40
- const userTable = /* @__PURE__ */ new Map();
6
+ const userTable = new Map();
41
7
  userTable.set("IoTester_1", "strong_password");
42
8
  userTable.set("IoTester_2", "strong_password");
9
+ // const strictUsername = new RegExp(/^[a-zA-Z0-9]{0,23}$/);
43
10
  function isAuthenticated(_ctx, clientId, username, password) {
44
- const pwd = utf8Decoder.decode(password);
45
- logger.info(
46
- `Verifying authentication of client '${clientId}' with username '${username}' and password '${pwd}'`,
47
- );
48
- return AuthenticationResult.ok;
11
+ const pwd = utf8Decoder.decode(password);
12
+ logger.info(`Verifying authentication of client '${clientId}' with username '${username}' and password '${pwd}'`);
13
+ return AuthenticationResult.ok;
14
+ // if (!userTable.has(username)) {
15
+ // if (!strictUsername.test(username)) {
16
+ // return AuthenticationResult.badUsernameOrPassword;
17
+ // }
18
+ // }
19
+ // const pass = userTable.get(username);
20
+ // if (pwd === pass) {
21
+ // return AuthenticationResult.ok;
22
+ // }
23
+ // return AuthenticationResult.badUsernameOrPassword;
49
24
  }
50
25
  function isAuthorizedToPublish(ctx, topic) {
51
- logger.debug(
52
- `Checking authorization of client '${ctx.store?.clientId}' to publish on topic '${topic}'`,
53
- );
54
- return true;
26
+ logger.debug(`Checking authorization of client '${ctx.store?.clientId}' to publish on topic '${topic}'`);
27
+ return true;
55
28
  }
56
29
  function isAuthorizedToSubscribe(ctx, topic) {
57
- logger.debug(
58
- `Checking authorization of client '${ctx.store?.clientId}' to subscribe to topic '${topic}'`,
59
- );
60
- return true;
30
+ logger.debug(`Checking authorization of client '${ctx.store?.clientId}' to subscribe to topic '${topic}'`);
31
+ return true;
61
32
  }
33
+ /** start the server **/
62
34
  const { _: [portNum] } = parseArgs(getArgs());
63
35
  const port = Number(portNum ?? 1883);
64
36
  const hostname = "::";
65
37
  logger.level(LogLevel.info);
66
38
  const tcpServer = new TcpServer({ port, hostname }, {
67
- handlers: {
68
- isAuthenticated,
69
- isAuthorizedToPublish,
70
- isAuthorizedToSubscribe,
71
- },
39
+ handlers: {
40
+ isAuthenticated,
41
+ isAuthorizedToPublish,
42
+ isAuthorizedToSubscribe,
43
+ },
72
44
  });
73
45
  tcpServer.start();
74
46
  logger.info(`Server started on port ${tcpServer.port}`);