@seriousme/opifex 1.4.0 → 1.5.1
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/CHANGELOG.md +18 -0
- package/bin/mqtt.ts +42 -5
- package/client/client.ts +7 -3
- package/client/handlers/handlePacket.ts +1 -1
- package/deno/client.ts +4 -4
- package/deno/server.ts +7 -0
- package/deno.json +1 -1
- package/dist/bin/mqtt.js +32 -5
- package/dist/client/client.js +6 -3
- package/dist/client/handlers/handlePacket.js +1 -1
- package/dist/node/client.js +8 -8
- package/dist/node/server.js +19 -1
- package/dist/server/handlers/handlePublish.js +2 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/node/client.ts +10 -8
- package/node/server.test.ts +82 -2
- package/node/server.ts +20 -1
- package/package.json +1 -1
- package/server/handlers/handlePublish.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,24 @@
|
|
|
4
4
|
|
|
5
5
|
### Changed
|
|
6
6
|
|
|
7
|
+
## [v1.5.1] 16-01-2025
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- fixed issue with QoS=1
|
|
12
|
+
|
|
13
|
+
## [v1.5.0] 02-01-2025
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- added client certificate support to mqtts.ts
|
|
18
|
+
|
|
19
|
+
## [v1.4.0] 02-01-2025
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- added client certificate support (w0otness)
|
|
24
|
+
|
|
7
25
|
## [v1.3.0] 19-11-2024
|
|
8
26
|
|
|
9
27
|
### Changed
|
package/bin/mqtt.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { DEFAULT_URL } from "../client/mod.ts";
|
|
3
3
|
import { logger, LogLevel } from "../utils/mod.ts";
|
|
4
4
|
import { getArgs, parseArgs } from "../utils/mod.ts";
|
|
5
|
-
import {
|
|
5
|
+
import { getFileData, TcpClient } from "../node/client.ts";
|
|
6
|
+
import type { Args } from "../utils/args.ts";
|
|
6
7
|
|
|
7
8
|
const client = new TcpClient();
|
|
8
9
|
const encoder = new TextEncoder();
|
|
@@ -21,18 +22,30 @@ const ConnectHelp = `
|
|
|
21
22
|
-i/--clientId the clientId to connect to the server
|
|
22
23
|
-U/--username the username to connect to the server
|
|
23
24
|
-P/--password the password to connect to the server
|
|
24
|
-
-
|
|
25
|
+
-C/--caFile the path to a CA certificate file
|
|
26
|
+
-c/--certFile the path to a certificate file
|
|
27
|
+
-k/--keyFile the path to a key file
|
|
25
28
|
-n/--noClean try to resume a previous session
|
|
26
29
|
-h/--help this text
|
|
27
30
|
`;
|
|
28
31
|
|
|
29
32
|
const connectOpts = {
|
|
30
|
-
string: [
|
|
33
|
+
string: [
|
|
34
|
+
"url",
|
|
35
|
+
"username",
|
|
36
|
+
"password",
|
|
37
|
+
"caFile",
|
|
38
|
+
"certFile",
|
|
39
|
+
"keyFile",
|
|
40
|
+
"clientId",
|
|
41
|
+
],
|
|
31
42
|
alias: {
|
|
32
43
|
u: "url",
|
|
33
44
|
U: "username",
|
|
34
45
|
P: "password",
|
|
46
|
+
C: "caFile",
|
|
35
47
|
c: "certFile",
|
|
48
|
+
k: "keyFile",
|
|
36
49
|
i: "clientId",
|
|
37
50
|
n: "noClean",
|
|
38
51
|
h: "help",
|
|
@@ -80,9 +93,25 @@ function parseQos(qosArg: string | number) {
|
|
|
80
93
|
return 0;
|
|
81
94
|
}
|
|
82
95
|
|
|
96
|
+
async function getTLSdata(connectArgs: Args) {
|
|
97
|
+
const caFileData = await getFileData(connectArgs.caFile);
|
|
98
|
+
const caCerts = caFileData ? [caFileData] : undefined;
|
|
99
|
+
const cert = await getFileData(connectArgs.certFile);
|
|
100
|
+
const key = await getFileData(connectArgs.keyFile);
|
|
101
|
+
return {
|
|
102
|
+
caCerts,
|
|
103
|
+
cert,
|
|
104
|
+
key,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
83
108
|
async function subscribe() {
|
|
84
109
|
const connectArgs = parseArgs(getArgs(), connectOpts);
|
|
85
|
-
const
|
|
110
|
+
const {
|
|
111
|
+
caCerts,
|
|
112
|
+
cert,
|
|
113
|
+
key,
|
|
114
|
+
} = await getTLSdata(connectArgs);
|
|
86
115
|
const subscribeArgs = parseArgs(getArgs(), subscribeOpts);
|
|
87
116
|
if (connectArgs.help) {
|
|
88
117
|
console.log(SubscribeHelp);
|
|
@@ -96,6 +125,8 @@ async function subscribe() {
|
|
|
96
125
|
await client.connect({
|
|
97
126
|
url: connectArgs.url,
|
|
98
127
|
caCerts,
|
|
128
|
+
cert,
|
|
129
|
+
key,
|
|
99
130
|
options: {
|
|
100
131
|
username: connectArgs.username,
|
|
101
132
|
password: encoder.encode(connectArgs.password),
|
|
@@ -157,7 +188,11 @@ const publishOpts = {
|
|
|
157
188
|
|
|
158
189
|
async function publish() {
|
|
159
190
|
const connectArgs = parseArgs(getArgs(), connectOpts);
|
|
160
|
-
const
|
|
191
|
+
const {
|
|
192
|
+
caCerts,
|
|
193
|
+
cert,
|
|
194
|
+
key,
|
|
195
|
+
} = await getTLSdata(connectArgs);
|
|
161
196
|
const publishArgs = parseArgs(getArgs(), publishOpts);
|
|
162
197
|
if (connectArgs.help) {
|
|
163
198
|
console.log(PublishHelp);
|
|
@@ -171,6 +206,8 @@ async function publish() {
|
|
|
171
206
|
await client.connect({
|
|
172
207
|
url: connectArgs.url,
|
|
173
208
|
caCerts,
|
|
209
|
+
cert,
|
|
210
|
+
key,
|
|
174
211
|
options: {
|
|
175
212
|
username: connectArgs.username,
|
|
176
213
|
password: encoder.encode(connectArgs.password),
|
package/client/client.ts
CHANGED
|
@@ -173,19 +173,23 @@ export class Client {
|
|
|
173
173
|
type: PacketType.publish,
|
|
174
174
|
...params,
|
|
175
175
|
};
|
|
176
|
-
await this.ctx.
|
|
176
|
+
await this.ctx.publish(packet);
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
async subscribe(params: SubscribeParameters): Promise<void> {
|
|
180
180
|
const packet: SubscribePacket = {
|
|
181
181
|
type: PacketType.subscribe,
|
|
182
|
-
id:
|
|
182
|
+
id: 0, //placeholder
|
|
183
183
|
...params,
|
|
184
184
|
};
|
|
185
|
-
await this.ctx.
|
|
185
|
+
await this.ctx.subscribe(packet);
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
async *messages(): AsyncGenerator<PublishPacket, void, unknown> {
|
|
189
189
|
yield* this.ctx.incoming;
|
|
190
190
|
}
|
|
191
|
+
|
|
192
|
+
closeMessages(reason?: string) {
|
|
193
|
+
this.ctx.incoming.close(reason);
|
|
194
|
+
}
|
|
191
195
|
}
|
|
@@ -25,7 +25,7 @@ export async function handlePacket(
|
|
|
25
25
|
ctx: Context,
|
|
26
26
|
packet: AnyPacket,
|
|
27
27
|
): Promise<void> {
|
|
28
|
-
logger.debug({ received:
|
|
28
|
+
logger.debug({ received: PacketNameByType[packet.type], packet });
|
|
29
29
|
if (ctx.connectionState !== ConnectionState.connected) {
|
|
30
30
|
if (packet.type === PacketType.connack) {
|
|
31
31
|
handleConnack(packet as ConnackPacket, ctx);
|
package/deno/client.ts
CHANGED
|
@@ -4,15 +4,15 @@ import { Client } from "../client/client.ts";
|
|
|
4
4
|
import { logger } from "../client/deps.ts";
|
|
5
5
|
import type { SockConn } from "../client/deps.ts";
|
|
6
6
|
|
|
7
|
-
export async function
|
|
7
|
+
export async function getFileData(filename: string | undefined) {
|
|
8
8
|
if (!filename) {
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
|
-
const
|
|
12
|
-
if (
|
|
11
|
+
const data = await Deno.readTextFile(filename);
|
|
12
|
+
if (data === "") {
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
|
-
return
|
|
15
|
+
return data;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export class TcpClient extends Client {
|
package/deno/server.ts
CHANGED
package/deno.json
CHANGED
package/dist/bin/mqtt.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { DEFAULT_URL } from "../client/mod.js";
|
|
3
3
|
import { logger, LogLevel } from "../utils/mod.js";
|
|
4
4
|
import { getArgs, parseArgs } from "../utils/mod.js";
|
|
5
|
-
import {
|
|
5
|
+
import { getFileData, TcpClient } from "../node/client.js";
|
|
6
6
|
const client = new TcpClient();
|
|
7
7
|
const encoder = new TextEncoder();
|
|
8
8
|
const decoder = new TextDecoder();
|
|
@@ -18,17 +18,29 @@ const ConnectHelp = `
|
|
|
18
18
|
-i/--clientId the clientId to connect to the server
|
|
19
19
|
-U/--username the username to connect to the server
|
|
20
20
|
-P/--password the password to connect to the server
|
|
21
|
-
-
|
|
21
|
+
-C/--caFile the path to a CA certificate file
|
|
22
|
+
-c/--certFile the path to a certificate file
|
|
23
|
+
-k/--keyFile the path to a key file
|
|
22
24
|
-n/--noClean try to resume a previous session
|
|
23
25
|
-h/--help this text
|
|
24
26
|
`;
|
|
25
27
|
const connectOpts = {
|
|
26
|
-
string: [
|
|
28
|
+
string: [
|
|
29
|
+
"url",
|
|
30
|
+
"username",
|
|
31
|
+
"password",
|
|
32
|
+
"caFile",
|
|
33
|
+
"certFile",
|
|
34
|
+
"keyFile",
|
|
35
|
+
"clientId",
|
|
36
|
+
],
|
|
27
37
|
alias: {
|
|
28
38
|
u: "url",
|
|
29
39
|
U: "username",
|
|
30
40
|
P: "password",
|
|
41
|
+
C: "caFile",
|
|
31
42
|
c: "certFile",
|
|
43
|
+
k: "keyFile",
|
|
32
44
|
i: "clientId",
|
|
33
45
|
n: "noClean",
|
|
34
46
|
h: "help",
|
|
@@ -72,9 +84,20 @@ function parseQos(qosArg) {
|
|
|
72
84
|
console.log("QoS must be between 0 and 2");
|
|
73
85
|
return 0;
|
|
74
86
|
}
|
|
87
|
+
async function getTLSdata(connectArgs) {
|
|
88
|
+
const caFileData = await getFileData(connectArgs.caFile);
|
|
89
|
+
const caCerts = caFileData ? [caFileData] : undefined;
|
|
90
|
+
const cert = await getFileData(connectArgs.certFile);
|
|
91
|
+
const key = await getFileData(connectArgs.keyFile);
|
|
92
|
+
return {
|
|
93
|
+
caCerts,
|
|
94
|
+
cert,
|
|
95
|
+
key,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
75
98
|
async function subscribe() {
|
|
76
99
|
const connectArgs = parseArgs(getArgs(), connectOpts);
|
|
77
|
-
const caCerts = await
|
|
100
|
+
const { caCerts, cert, key, } = await getTLSdata(connectArgs);
|
|
78
101
|
const subscribeArgs = parseArgs(getArgs(), subscribeOpts);
|
|
79
102
|
if (connectArgs.help) {
|
|
80
103
|
console.log(SubscribeHelp);
|
|
@@ -88,6 +111,8 @@ async function subscribe() {
|
|
|
88
111
|
await client.connect({
|
|
89
112
|
url: connectArgs.url,
|
|
90
113
|
caCerts,
|
|
114
|
+
cert,
|
|
115
|
+
key,
|
|
91
116
|
options: {
|
|
92
117
|
username: connectArgs.username,
|
|
93
118
|
password: encoder.encode(connectArgs.password),
|
|
@@ -145,7 +170,7 @@ const publishOpts = {
|
|
|
145
170
|
};
|
|
146
171
|
async function publish() {
|
|
147
172
|
const connectArgs = parseArgs(getArgs(), connectOpts);
|
|
148
|
-
const caCerts = await
|
|
173
|
+
const { caCerts, cert, key, } = await getTLSdata(connectArgs);
|
|
149
174
|
const publishArgs = parseArgs(getArgs(), publishOpts);
|
|
150
175
|
if (connectArgs.help) {
|
|
151
176
|
console.log(PublishHelp);
|
|
@@ -159,6 +184,8 @@ async function publish() {
|
|
|
159
184
|
await client.connect({
|
|
160
185
|
url: connectArgs.url,
|
|
161
186
|
caCerts,
|
|
187
|
+
cert,
|
|
188
|
+
key,
|
|
162
189
|
options: {
|
|
163
190
|
username: connectArgs.username,
|
|
164
191
|
password: encoder.encode(connectArgs.password),
|
package/dist/client/client.js
CHANGED
|
@@ -113,17 +113,20 @@ export class Client {
|
|
|
113
113
|
type: PacketType.publish,
|
|
114
114
|
...params,
|
|
115
115
|
};
|
|
116
|
-
await this.ctx.
|
|
116
|
+
await this.ctx.publish(packet);
|
|
117
117
|
}
|
|
118
118
|
async subscribe(params) {
|
|
119
119
|
const packet = {
|
|
120
120
|
type: PacketType.subscribe,
|
|
121
|
-
id:
|
|
121
|
+
id: 0, //placeholder
|
|
122
122
|
...params,
|
|
123
123
|
};
|
|
124
|
-
await this.ctx.
|
|
124
|
+
await this.ctx.subscribe(packet);
|
|
125
125
|
}
|
|
126
126
|
async *messages() {
|
|
127
127
|
yield* this.ctx.incoming;
|
|
128
128
|
}
|
|
129
|
+
closeMessages(reason) {
|
|
130
|
+
this.ctx.incoming.close(reason);
|
|
131
|
+
}
|
|
129
132
|
}
|
|
@@ -9,7 +9,7 @@ import { handleSuback } from "./handleSuback.js";
|
|
|
9
9
|
import { handleUnsuback } from "./handleUnsuback.js";
|
|
10
10
|
import { logger, PacketNameByType, PacketType } from "../deps.js";
|
|
11
11
|
export async function handlePacket(ctx, packet) {
|
|
12
|
-
logger.debug({ received:
|
|
12
|
+
logger.debug({ received: PacketNameByType[packet.type], packet });
|
|
13
13
|
if (ctx.connectionState !== ConnectionState.connected) {
|
|
14
14
|
if (packet.type === PacketType.connack) {
|
|
15
15
|
handleConnack(packet, ctx);
|
package/dist/node/client.js
CHANGED
|
@@ -6,15 +6,15 @@ import { readFile } from "node:fs/promises";
|
|
|
6
6
|
import { createConnection } from "node:net";
|
|
7
7
|
import * as tls from "node:tls";
|
|
8
8
|
import { wrapNodeSocket } from "./wrapNodeSocket.js";
|
|
9
|
-
export async function
|
|
9
|
+
export async function getFileData(filename) {
|
|
10
10
|
if (!filename) {
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
|
-
const
|
|
14
|
-
if (
|
|
13
|
+
const data = await readFile(filename, { encoding: "utf-8" });
|
|
14
|
+
if (data === "") {
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
|
-
return
|
|
17
|
+
return data;
|
|
18
18
|
}
|
|
19
19
|
export class TcpClient extends Client {
|
|
20
20
|
connectMQTT(hostname, port = 1883) {
|
|
@@ -30,15 +30,15 @@ export class TcpClient extends Client {
|
|
|
30
30
|
});
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
|
-
connectMQTTS(hostname, port = 8883,
|
|
33
|
+
connectMQTTS(hostname, port = 8883, ca, cert, key) {
|
|
34
34
|
const opts = {
|
|
35
35
|
host: hostname,
|
|
36
36
|
port,
|
|
37
|
-
secureContext:
|
|
38
|
-
? tls.createSecureContext({ cert
|
|
37
|
+
secureContext: (ca || key || cert)
|
|
38
|
+
? tls.createSecureContext({ ca, cert, key })
|
|
39
39
|
: undefined,
|
|
40
40
|
};
|
|
41
|
-
logger.debug({ hostname, port,
|
|
41
|
+
logger.debug({ hostname, port, ca, cert });
|
|
42
42
|
return new Promise((resolve, reject) => {
|
|
43
43
|
const socket = tls.connect(opts, () => {
|
|
44
44
|
logger.debug("Connected to server");
|
package/dist/node/server.js
CHANGED
|
@@ -17,6 +17,24 @@ export class TcpServer {
|
|
|
17
17
|
this.server?.close();
|
|
18
18
|
}
|
|
19
19
|
get port() {
|
|
20
|
-
|
|
20
|
+
const address = this.server?.address();
|
|
21
|
+
if (typeof address === "object") {
|
|
22
|
+
return address?.port;
|
|
23
|
+
}
|
|
24
|
+
return this.serverOptions?.port;
|
|
25
|
+
}
|
|
26
|
+
get address() {
|
|
27
|
+
const addressResult = this.server?.address();
|
|
28
|
+
if (typeof addressResult === "object") {
|
|
29
|
+
const address = addressResult?.address;
|
|
30
|
+
if (address === "::") {
|
|
31
|
+
return "localhost";
|
|
32
|
+
}
|
|
33
|
+
if (address?.includes(":")) {
|
|
34
|
+
return `[${address}]`;
|
|
35
|
+
}
|
|
36
|
+
return address;
|
|
37
|
+
}
|
|
38
|
+
return addressResult;
|
|
21
39
|
}
|
|
22
40
|
}
|
|
@@ -21,10 +21,11 @@ export async function handlePublish(ctx, packet) {
|
|
|
21
21
|
if (packet.id !== undefined) {
|
|
22
22
|
// qos 1
|
|
23
23
|
if (qos === 1) {
|
|
24
|
+
const id = packet.id; // retain the id
|
|
24
25
|
ctx.persistence.publish(packet.topic, packet);
|
|
25
26
|
await ctx.send({
|
|
26
27
|
type: PacketType.puback,
|
|
27
|
-
id
|
|
28
|
+
id,
|
|
28
29
|
});
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../bin/conformanceServer.ts","../bin/demoServer.ts","../bin/mqtt.ts"],"version":"5.7.
|
|
1
|
+
{"root":["../bin/conformanceServer.ts","../bin/demoServer.ts","../bin/mqtt.ts"],"version":"5.7.3"}
|
package/node/client.ts
CHANGED
|
@@ -8,15 +8,15 @@ import * as tls from "node:tls";
|
|
|
8
8
|
import type { SockConn } from "../socket/socket.ts";
|
|
9
9
|
import { wrapNodeSocket } from "./wrapNodeSocket.ts";
|
|
10
10
|
|
|
11
|
-
export async function
|
|
11
|
+
export async function getFileData(filename: string | undefined) {
|
|
12
12
|
if (!filename) {
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
|
-
const
|
|
16
|
-
if (
|
|
15
|
+
const data = await readFile(filename, { encoding: "utf-8" });
|
|
16
|
+
if (data === "") {
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
|
-
return
|
|
19
|
+
return data;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export class TcpClient extends Client {
|
|
@@ -40,16 +40,18 @@ export class TcpClient extends Client {
|
|
|
40
40
|
protected connectMQTTS(
|
|
41
41
|
hostname: string,
|
|
42
42
|
port = 8883,
|
|
43
|
-
|
|
43
|
+
ca?: string[],
|
|
44
|
+
cert?: string,
|
|
45
|
+
key?: string,
|
|
44
46
|
): Promise<SockConn> {
|
|
45
47
|
const opts = {
|
|
46
48
|
host: hostname,
|
|
47
49
|
port,
|
|
48
|
-
secureContext:
|
|
49
|
-
? tls.createSecureContext({ cert
|
|
50
|
+
secureContext: (ca || key || cert)
|
|
51
|
+
? tls.createSecureContext({ ca, cert, key })
|
|
50
52
|
: undefined,
|
|
51
53
|
};
|
|
52
|
-
logger.debug({ hostname, port,
|
|
54
|
+
logger.debug({ hostname, port, ca, cert });
|
|
53
55
|
return new Promise((resolve, reject) => {
|
|
54
56
|
const socket = tls.connect(opts, () => {
|
|
55
57
|
logger.debug("Connected to server");
|
package/node/server.test.ts
CHANGED
|
@@ -1,14 +1,94 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
1
|
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { TcpClient } from "./client.ts";
|
|
3
4
|
import { TcpServer } from "./server.ts";
|
|
5
|
+
import { logger, LogLevel } from "../utils/mod.ts";
|
|
6
|
+
import type { PublishPacket, QoS } from "../mqttPacket/mod.ts";
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
logger.level(LogLevel.info);
|
|
9
|
+
export function sleep(ms: number): Promise<unknown> {
|
|
10
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
11
|
+
}
|
|
12
|
+
export async function serverTest() {
|
|
6
13
|
const server = new TcpServer({ port: 0 }, {});
|
|
7
14
|
server.start();
|
|
15
|
+
await sleep(1000);
|
|
16
|
+
|
|
8
17
|
assert.deepStrictEqual(
|
|
9
18
|
server.port !== undefined,
|
|
10
19
|
true,
|
|
11
20
|
"server runs a a random port",
|
|
12
21
|
);
|
|
22
|
+
logger.verbose({ port: server.port, address: server.address });
|
|
23
|
+
|
|
24
|
+
const params = {
|
|
25
|
+
url: new URL(`mqtt://${server.address}:${server.port}`),
|
|
26
|
+
numberOfRetries: 0,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const client = new TcpClient();
|
|
30
|
+
|
|
31
|
+
await client.connect(params);
|
|
32
|
+
assert(true, "Client connected to server");
|
|
33
|
+
|
|
34
|
+
const publishSet: { topic: string; qos: QoS }[] = [
|
|
35
|
+
{ topic: "t0@q0", qos: 0 },
|
|
36
|
+
{ topic: "t1@q0", qos: 0 },
|
|
37
|
+
{ topic: "t2@q0", qos: 0 },
|
|
38
|
+
{ topic: "t0@q1", qos: 1 },
|
|
39
|
+
{ topic: "t1@q1", qos: 1 },
|
|
40
|
+
{ topic: "t2@q1", qos: 1 },
|
|
41
|
+
{ topic: "t0@q2", qos: 2 },
|
|
42
|
+
{ topic: "t1@q2", qos: 2 },
|
|
43
|
+
{ topic: "t2@q2", qos: 2 },
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
const subscriptions = publishSet.map((item) => ({
|
|
47
|
+
topicFilter: item.topic,
|
|
48
|
+
qos: item.qos,
|
|
49
|
+
}));
|
|
50
|
+
await client.subscribe({
|
|
51
|
+
subscriptions,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// the IIFE ensures message reception runs in parallel
|
|
55
|
+
logger.verbose(`Start receiving`);
|
|
56
|
+
const received: PublishPacket[] = [];
|
|
57
|
+
(async function () {
|
|
58
|
+
for await (const item of client.messages()) {
|
|
59
|
+
logger.verbose(`Receiving: ${item.topic} -- ${item.qos}`);
|
|
60
|
+
received.push(item);
|
|
61
|
+
}
|
|
62
|
+
})();
|
|
63
|
+
// end of IIFE
|
|
64
|
+
for (const item of publishSet) {
|
|
65
|
+
logger.verbose(`Publishing: ${item.topic} -- ${item.qos}`);
|
|
66
|
+
await client.publish({
|
|
67
|
+
topic: item.topic,
|
|
68
|
+
qos: item.qos,
|
|
69
|
+
payload: new Uint8Array([0x01]),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
await sleep(100);
|
|
74
|
+
logger.verbose(`Disconnect client`);
|
|
75
|
+
//client.closeMessages();
|
|
76
|
+
await client.disconnect();
|
|
77
|
+
|
|
78
|
+
logger.verbose(`Check completeness`);
|
|
79
|
+
for (const item of publishSet) {
|
|
80
|
+
const found = received.find((f) =>
|
|
81
|
+
f.topic == item.topic && f.qos === item.qos
|
|
82
|
+
);
|
|
83
|
+
logger.verbose(`Found: ${item.topic} -- ${item.qos}`);
|
|
84
|
+
assert(found, `${item.topic} -- ${item.qos}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
logger.verbose(`Stop server`);
|
|
13
88
|
server.stop();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
test("Test pubSub using client and server", async function () {
|
|
92
|
+
await serverTest();
|
|
93
|
+
logger.verbose("End of test");
|
|
14
94
|
});
|
package/node/server.ts
CHANGED
|
@@ -31,6 +31,25 @@ export class TcpServer {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
get port() {
|
|
34
|
-
|
|
34
|
+
const address = this.server?.address();
|
|
35
|
+
if (typeof address === "object") {
|
|
36
|
+
return address?.port;
|
|
37
|
+
}
|
|
38
|
+
return this.serverOptions?.port;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get address() {
|
|
42
|
+
const addressResult = this.server?.address();
|
|
43
|
+
if (typeof addressResult === "object") {
|
|
44
|
+
const address = addressResult?.address;
|
|
45
|
+
if (address === "::") {
|
|
46
|
+
return "localhost";
|
|
47
|
+
}
|
|
48
|
+
if (address?.includes(":")) {
|
|
49
|
+
return `[${address}]`;
|
|
50
|
+
}
|
|
51
|
+
return address;
|
|
52
|
+
}
|
|
53
|
+
return addressResult;
|
|
35
54
|
}
|
|
36
55
|
}
|
package/package.json
CHANGED
|
@@ -28,10 +28,11 @@ export async function handlePublish(
|
|
|
28
28
|
if (packet.id !== undefined) {
|
|
29
29
|
// qos 1
|
|
30
30
|
if (qos === 1) {
|
|
31
|
+
const id = packet.id; // retain the id
|
|
31
32
|
ctx.persistence.publish(packet.topic, packet);
|
|
32
33
|
await ctx.send({
|
|
33
34
|
type: PacketType.puback,
|
|
34
|
-
id
|
|
35
|
+
id,
|
|
35
36
|
});
|
|
36
37
|
return;
|
|
37
38
|
}
|