@seriousme/opifex 2.2.0 → 2.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.
- package/README.md +7 -6
- package/bin/mqtt.ts +1 -1
- package/client/client.ts +62 -7
- package/client/context.ts +2 -2
- package/client/deps.ts +3 -3
- package/client/mod.ts +2 -2
- package/connNext/baseConnNext.ts +123 -0
- package/connNext/connNext.ts +44 -0
- package/deno/README.md +1 -1
- package/deno/denoConnToNext.ts +104 -0
- package/deno/tcpClient.ts +8 -24
- package/deno/tcpServer.test.ts +14 -103
- package/deno/tcpServer.ts +14 -6
- package/deno/tlsServer.test.ts +14 -58
- package/deno/tlsServer.ts +14 -6
- package/deno/wsServer.test.ts +15 -61
- package/deno/wsServer.ts +82 -32
- package/dev_utils/WebStreamEndpoint.ts +5 -0
- package/dev_utils/asyncStreamEndpoint.ts +5 -0
- package/dev_utils/asyncStreamPair.ts +109 -0
- package/dev_utils/asyncStreamPairToNext.ts +45 -0
- package/dev_utils/asyncStreamToNext.ts +64 -0
- package/dev_utils/connNextPair.test.ts +179 -0
- package/dev_utils/mockConn.ts +59 -29
- package/dev_utils/mockServer.ts +3 -4
- package/dev_utils/mod.ts +1 -2
- package/dev_utils/testClient.ts +64 -0
- package/dev_utils/webStreamPair.ts +3 -7
- package/dev_utils/webStreamPairToNext.ts +48 -0
- package/dev_utils/webStreamToNext.ts +54 -0
- package/dist/bin/mqtt.js +1 -1
- package/dist/bin/mqtt.js.map +1 -1
- package/dist/client/client.d.ts +24 -3
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +45 -5
- package/dist/client/client.js.map +1 -1
- package/dist/client/context.d.ts +2 -2
- package/dist/client/context.d.ts.map +1 -1
- package/dist/client/deps.d.ts +1 -1
- package/dist/client/deps.d.ts.map +1 -1
- package/dist/client/mod.d.ts +1 -1
- package/dist/client/mod.d.ts.map +1 -1
- package/dist/connNext/baseConnNext.d.ts +20 -0
- package/dist/connNext/baseConnNext.d.ts.map +1 -0
- package/dist/connNext/baseConnNext.js +95 -0
- package/dist/connNext/baseConnNext.js.map +1 -0
- package/dist/connNext/connNext.d.ts +43 -0
- package/dist/connNext/connNext.d.ts.map +1 -0
- package/dist/connNext/connNext.js +2 -0
- package/dist/connNext/connNext.js.map +1 -0
- package/dist/examples/authHandler.test.js +2 -2
- package/dist/examples/authHandler.test.js.map +1 -1
- package/dist/examples/mqtt+mqtts.test.d.ts +2 -0
- package/dist/examples/mqtt+mqtts.test.d.ts.map +1 -0
- package/dist/examples/mqtt+mqtts.test.js +104 -0
- package/dist/examples/mqtt+mqtts.test.js.map +1 -0
- package/dist/examples/mqtt.test.js +2 -2
- package/dist/examples/mqtt.test.js.map +1 -1
- package/dist/examples/mqtts.test.js +2 -2
- package/dist/examples/mqtts.test.js.map +1 -1
- package/dist/mqttConn/mqttConn.d.ts +5 -6
- package/dist/mqttConn/mqttConn.d.ts.map +1 -1
- package/dist/mqttConn/mqttConn.js +2 -3
- package/dist/mqttConn/mqttConn.js.map +1 -1
- package/dist/node/nodeSocketToNext.d.ts +17 -0
- package/dist/node/nodeSocketToNext.d.ts.map +1 -0
- package/dist/node/nodeSocketToNext.js +105 -0
- package/dist/node/nodeSocketToNext.js.map +1 -0
- package/dist/node/tcpClient.d.ts +4 -6
- package/dist/node/tcpClient.d.ts.map +1 -1
- package/dist/node/tcpClient.js +6 -21
- package/dist/node/tcpClient.js.map +1 -1
- package/dist/node/tcpServer.d.ts +8 -4
- package/dist/node/tcpServer.d.ts.map +1 -1
- package/dist/node/tcpServer.js +14 -6
- package/dist/node/tcpServer.js.map +1 -1
- package/dist/node/tlsServer.d.ts +7 -3
- package/dist/node/tlsServer.d.ts.map +1 -1
- package/dist/node/tlsServer.js +13 -5
- package/dist/node/tlsServer.js.map +1 -1
- package/dist/server/context.d.ts +5 -5
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/context.js +4 -1
- package/dist/server/context.js.map +1 -1
- package/dist/server/deps.d.ts +1 -1
- package/dist/server/deps.d.ts.map +1 -1
- package/dist/server/mod.d.ts +1 -1
- package/dist/server/mod.d.ts.map +1 -1
- package/dist/server/server.d.ts +3 -3
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +3 -3
- package/dist/utils/utils.d.ts +1 -2
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +3 -2
- package/dist/utils/utils.js.map +1 -1
- package/dist/web/webSocketToNext.d.ts +20 -0
- package/dist/web/webSocketToNext.d.ts.map +1 -0
- package/dist/web/webSocketToNext.js +125 -0
- package/dist/web/webSocketToNext.js.map +1 -0
- package/dist/web/wsClient.d.ts +3 -3
- package/dist/web/wsClient.d.ts.map +1 -1
- package/dist/web/wsClient.js +22 -7
- package/dist/web/wsClient.js.map +1 -1
- package/dist/web/wsClient.test.js +15 -0
- package/dist/web/wsClient.test.js.map +1 -1
- package/examples/README.md +21 -0
- package/examples/authHandler.test.ts +2 -2
- package/examples/mqtt+mqtts.test.ts +122 -0
- package/examples/mqtt.test.ts +2 -2
- package/examples/mqtts.test.ts +2 -2
- package/examples/webSocket.test.ts +10 -64
- package/examples/webSocketServer.ts +98 -0
- package/mqttConn/mqttConn.test.ts +6 -6
- package/mqttConn/mqttConn.ts +8 -9
- package/node/README.md +1 -1
- package/node/nodeSocketToNext.ts +124 -0
- package/node/tcpClient.ts +13 -27
- package/node/tcpServer.test.ts +12 -58
- package/node/tcpServer.ts +18 -8
- package/node/tlsServer.test.ts +11 -58
- package/node/tlsServer.ts +17 -7
- package/package.json +4 -3
- package/server/context.ts +9 -6
- package/server/deps.ts +1 -1
- package/server/mod.ts +1 -1
- package/server/server.ts +6 -6
- package/utils/utils.ts +3 -2
- package/web/webSocketToNext.ts +146 -0
- package/web/wsClient.test.ts +22 -0
- package/web/wsClient.ts +29 -9
- package/dev_utils/webStreamPair.test.ts +0 -171
- package/dist/node/wrapNodeSocket.d.ts +0 -12
- package/dist/node/wrapNodeSocket.d.ts.map +0 -1
- package/dist/node/wrapNodeSocket.js +0 -36
- package/dist/node/wrapNodeSocket.js.map +0 -1
- package/dist/socket/socket.d.ts +0 -58
- package/dist/socket/socket.d.ts.map +0 -1
- package/dist/socket/socket.js +0 -99
- package/dist/socket/socket.js.map +0 -1
- package/dist/web/webSocketStream.d.ts +0 -29
- package/dist/web/webSocketStream.d.ts.map +0 -1
- package/dist/web/webSocketStream.js +0 -69
- package/dist/web/webSocketStream.js.map +0 -1
- package/dist/web/wrapWebSocket.d.ts +0 -12
- package/dist/web/wrapWebSocket.d.ts.map +0 -1
- package/dist/web/wrapWebSocket.js +0 -41
- package/dist/web/wrapWebSocket.js.map +0 -1
- package/node/socket-node-stream.test.ts +0 -322
- package/node/socket-node-stream.ts +0 -174
- package/node/wrapNodeSocket.ts +0 -44
- package/socket/socket.test.ts +0 -91
- package/socket/socket.ts +0 -149
- package/web/webSocketStream.ts +0 -97
- package/web/wrapWebSocket.ts +0 -52
package/deno/tcpServer.test.ts
CHANGED
|
@@ -2,8 +2,10 @@ import { test } from "node:test";
|
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import { TcpClient } from "./tcpClient.ts";
|
|
4
4
|
import { TcpServer } from "./tcpServer.ts";
|
|
5
|
-
import {
|
|
6
|
-
import type { ProtocolLevel
|
|
5
|
+
import { logger, LogLevel } from "../utils/mod.ts";
|
|
6
|
+
import type { ProtocolLevel } from "../mqttPacket/mod.ts";
|
|
7
|
+
import { testClient } from "../dev_utils/testClient.ts";
|
|
8
|
+
import { MqttServer } from "../server/mod.ts";
|
|
7
9
|
|
|
8
10
|
logger.level(LogLevel.info);
|
|
9
11
|
|
|
@@ -24,64 +26,10 @@ test("Deno: Test pubSub using client and server", async () => {
|
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
const client = new TcpClient();
|
|
27
|
-
|
|
28
|
-
await client.connect(params);
|
|
29
|
-
assert(true, "Client connected to server");
|
|
30
|
-
|
|
31
|
-
const publishSet: { topic: string; qos: QoS }[] = [
|
|
32
|
-
{ topic: "t0@q0", qos: 0 },
|
|
33
|
-
{ topic: "t1@q0", qos: 0 },
|
|
34
|
-
{ topic: "t2@q0", qos: 0 },
|
|
35
|
-
{ topic: "t0@q1", qos: 1 },
|
|
36
|
-
{ topic: "t1@q1", qos: 1 },
|
|
37
|
-
{ topic: "t2@q1", qos: 1 },
|
|
38
|
-
{ topic: "t0@q2", qos: 2 },
|
|
39
|
-
{ topic: "t1@q2", qos: 2 },
|
|
40
|
-
{ topic: "t2@q2", qos: 2 },
|
|
41
|
-
];
|
|
42
|
-
|
|
43
|
-
const subscriptions = publishSet.map((item) => ({
|
|
44
|
-
topicFilter: item.topic,
|
|
45
|
-
qos: item.qos,
|
|
46
|
-
}));
|
|
47
|
-
await client.subscribe({
|
|
48
|
-
subscriptions,
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
// the IIFE ensures message reception runs in parallel
|
|
52
|
-
logger.verbose("Start receiving");
|
|
53
|
-
const received: PublishPacket[] = [];
|
|
54
|
-
(async () => {
|
|
55
|
-
for await (const item of client.messages()) {
|
|
56
|
-
logger.verbose("Receiving:", item.topic, "--", item.qos);
|
|
57
|
-
received.push(item);
|
|
58
|
-
}
|
|
59
|
-
})();
|
|
60
|
-
// end of IIFE
|
|
61
|
-
for (const item of publishSet) {
|
|
62
|
-
logger.verbose("Publishing:", item.topic, "--", item.qos);
|
|
63
|
-
await client.publish({
|
|
64
|
-
topic: item.topic,
|
|
65
|
-
qos: item.qos,
|
|
66
|
-
payload: new Uint8Array([0x01]),
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
await delay(100);
|
|
71
|
-
logger.verbose("Disconnect client");
|
|
72
|
-
await client.disconnect();
|
|
73
|
-
|
|
74
|
-
logger.verbose("Check completeness");
|
|
75
|
-
for (const item of publishSet) {
|
|
76
|
-
const found = received.find((f) =>
|
|
77
|
-
f.topic == item.topic && f.qos === item.qos
|
|
78
|
-
);
|
|
79
|
-
logger.verbose("Found:", item.topic, "--", item.qos);
|
|
80
|
-
assert(found, `${item.topic} -- ${item.qos}`);
|
|
81
|
-
}
|
|
29
|
+
await testClient(client, params);
|
|
82
30
|
|
|
83
31
|
logger.verbose("Stop server");
|
|
84
|
-
server.stop();
|
|
32
|
+
await server.stop();
|
|
85
33
|
});
|
|
86
34
|
|
|
87
35
|
test("Deno: Test subscription persistence after reconnect", async () => {
|
|
@@ -99,51 +47,14 @@ test("Deno: Test subscription persistence after reconnect", async () => {
|
|
|
99
47
|
};
|
|
100
48
|
|
|
101
49
|
const client = new TcpClient();
|
|
102
|
-
|
|
103
|
-
const received: PublishPacket[] = [];
|
|
104
|
-
|
|
105
|
-
// First connection and subscription
|
|
106
|
-
await client.connect(params);
|
|
107
|
-
await client.subscribe({
|
|
108
|
-
subscriptions: [{
|
|
109
|
-
topicFilter: testTopic,
|
|
110
|
-
qos: 0,
|
|
111
|
-
}],
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
// Start receiving messages
|
|
115
|
-
(async () => {
|
|
116
|
-
for await (const item of client.messages()) {
|
|
117
|
-
received.push(item);
|
|
118
|
-
}
|
|
119
|
-
})();
|
|
50
|
+
await testClient(client, params);
|
|
120
51
|
|
|
121
|
-
|
|
122
|
-
await
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// Reconnect client
|
|
126
|
-
await client.connect(params);
|
|
127
|
-
await delay(100);
|
|
128
|
-
|
|
129
|
-
// Publish test message
|
|
130
|
-
await client.publish({
|
|
131
|
-
topic: testTopic,
|
|
132
|
-
qos: 0,
|
|
133
|
-
payload: new Uint8Array([0x01]),
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
await delay(100);
|
|
137
|
-
logger.verbose("Disconnect client");
|
|
138
|
-
await client.disconnect();
|
|
139
|
-
|
|
140
|
-
// Verify message was received
|
|
141
|
-
assert.equal(received.length, 1, "Should receive one message");
|
|
142
|
-
assert.equal(
|
|
143
|
-
received[0].topic,
|
|
144
|
-
testTopic,
|
|
145
|
-
"Should receive message on subscribed topic",
|
|
146
|
-
);
|
|
52
|
+
logger.verbose("Stop server");
|
|
53
|
+
await server.stop();
|
|
54
|
+
});
|
|
147
55
|
|
|
148
|
-
|
|
56
|
+
test("Deno: Passing a mqttServer works", () => {
|
|
57
|
+
const mqttServer = new MqttServer({});
|
|
58
|
+
const server = new TcpServer({ port: 0 }, mqttServer);
|
|
59
|
+
assert(server.mqttServer === mqttServer);
|
|
149
60
|
});
|
package/deno/tcpServer.ts
CHANGED
|
@@ -5,24 +5,30 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { MqttServer } from "../server/mod.ts";
|
|
8
|
+
import { denoConnToNext } from "./denoConnToNext.ts";
|
|
8
9
|
import type { MqttServerOptions } from "../server/mod.ts";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* TCP server that wraps a MqttServer, see the /examples folder
|
|
12
13
|
*/
|
|
13
14
|
export class TcpServer {
|
|
15
|
+
/** the MqttServer instance used by the server */
|
|
16
|
+
readonly mqttServer: MqttServer;
|
|
14
17
|
private listener: Deno.Listener<Deno.Conn>;
|
|
15
|
-
private mqttServer: MqttServer;
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* Create a new TCP server
|
|
19
21
|
*/
|
|
20
22
|
constructor(
|
|
21
23
|
serverOptions: Deno.TcpListenOptions,
|
|
22
|
-
mqttOptions: MqttServerOptions,
|
|
24
|
+
mqttOptions: MqttServerOptions | MqttServer,
|
|
23
25
|
) {
|
|
24
26
|
this.listener = Deno.listen(serverOptions);
|
|
25
|
-
|
|
27
|
+
if (mqttOptions instanceof MqttServer) {
|
|
28
|
+
this.mqttServer = mqttOptions;
|
|
29
|
+
} else {
|
|
30
|
+
this.mqttServer = new MqttServer(mqttOptions);
|
|
31
|
+
}
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
/**
|
|
@@ -30,15 +36,17 @@ export class TcpServer {
|
|
|
30
36
|
*/
|
|
31
37
|
async start(): Promise<void> {
|
|
32
38
|
for await (const conn of this.listener) {
|
|
33
|
-
this.mqttServer.serve(conn);
|
|
39
|
+
this.mqttServer.serve(denoConnToNext(conn));
|
|
34
40
|
}
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
/**
|
|
38
44
|
* Stop listening
|
|
39
45
|
*/
|
|
40
|
-
stop(): void {
|
|
41
|
-
|
|
46
|
+
async stop(opts = { closeMqtt: true }): Promise<void> {
|
|
47
|
+
if (opts.closeMqtt) {
|
|
48
|
+
await this.mqttServer.close();
|
|
49
|
+
}
|
|
42
50
|
this.listener.close();
|
|
43
51
|
}
|
|
44
52
|
|
package/deno/tlsServer.test.ts
CHANGED
|
@@ -3,8 +3,9 @@ import assert from "node:assert/strict";
|
|
|
3
3
|
import { TcpClient } from "./tcpClient.ts";
|
|
4
4
|
import { TlsServer } from "./tlsServer.ts";
|
|
5
5
|
import { logger, LogLevel } from "../utils/mod.ts";
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
6
|
+
import { generateLocalhostCerts } from "../dev_utils/mod.ts";
|
|
7
|
+
import { testClient } from "../dev_utils/testClient.ts";
|
|
8
|
+
import { MqttServer } from "../server/mod.ts";
|
|
8
9
|
|
|
9
10
|
logger.level(LogLevel.info);
|
|
10
11
|
|
|
@@ -36,62 +37,17 @@ test("Deno: Test pubSub using client and server", async () => {
|
|
|
36
37
|
logger.verbose("client parameters: ", params);
|
|
37
38
|
|
|
38
39
|
const client = new TcpClient();
|
|
39
|
-
|
|
40
|
-
await client.connect(params);
|
|
41
|
-
assert(true, "Client connected to server");
|
|
42
|
-
|
|
43
|
-
const publishSet: { topic: string; qos: QoS }[] = [
|
|
44
|
-
{ topic: "t0@q0", qos: 0 },
|
|
45
|
-
{ topic: "t1@q0", qos: 0 },
|
|
46
|
-
{ topic: "t2@q0", qos: 0 },
|
|
47
|
-
{ topic: "t0@q1", qos: 1 },
|
|
48
|
-
{ topic: "t1@q1", qos: 1 },
|
|
49
|
-
{ topic: "t2@q1", qos: 1 },
|
|
50
|
-
{ topic: "t0@q2", qos: 2 },
|
|
51
|
-
{ topic: "t1@q2", qos: 2 },
|
|
52
|
-
{ topic: "t2@q2", qos: 2 },
|
|
53
|
-
];
|
|
54
|
-
|
|
55
|
-
const subscriptions = publishSet.map((item) => ({
|
|
56
|
-
topicFilter: item.topic,
|
|
57
|
-
qos: item.qos,
|
|
58
|
-
}));
|
|
59
|
-
await client.subscribe({
|
|
60
|
-
subscriptions,
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// the IIFE ensures message reception runs in parallel
|
|
64
|
-
logger.verbose("Start receiving");
|
|
65
|
-
const received: PublishPacket[] = [];
|
|
66
|
-
(async () => {
|
|
67
|
-
for await (const item of client.messages()) {
|
|
68
|
-
logger.verbose("Receiving:", item.topic, "--", item.qos);
|
|
69
|
-
received.push(item);
|
|
70
|
-
}
|
|
71
|
-
})();
|
|
72
|
-
// end of IIFE
|
|
73
|
-
for (const item of publishSet) {
|
|
74
|
-
logger.verbose("Publishing:", item.topic, "--", item.qos);
|
|
75
|
-
await client.publish({
|
|
76
|
-
topic: item.topic,
|
|
77
|
-
qos: item.qos,
|
|
78
|
-
payload: new Uint8Array([0x01]),
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
await delay(100);
|
|
83
|
-
logger.verbose("Disconnect client");
|
|
84
|
-
await client.disconnect();
|
|
85
|
-
|
|
86
|
-
logger.verbose("Check completeness");
|
|
87
|
-
for (const item of publishSet) {
|
|
88
|
-
const found = received.find((f) =>
|
|
89
|
-
f.topic == item.topic && f.qos === item.qos
|
|
90
|
-
);
|
|
91
|
-
logger.verbose("Found:", item.topic, "--", item.qos);
|
|
92
|
-
assert(found, `${item.topic} -- ${item.qos}`);
|
|
93
|
-
}
|
|
40
|
+
await testClient(client, params);
|
|
94
41
|
|
|
95
42
|
logger.verbose("Stop server");
|
|
96
|
-
server.stop();
|
|
43
|
+
await server.stop();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("Deno: Passing a mqttServer works", () => {
|
|
47
|
+
const mqttServer = new MqttServer({});
|
|
48
|
+
const server = new TlsServer(
|
|
49
|
+
{ hostname: "localhost", port: 0, key, cert },
|
|
50
|
+
mqttServer,
|
|
51
|
+
);
|
|
52
|
+
assert(server.mqttServer === mqttServer);
|
|
97
53
|
});
|
package/deno/tlsServer.ts
CHANGED
|
@@ -6,22 +6,28 @@
|
|
|
6
6
|
|
|
7
7
|
import { MqttServer } from "../server/mod.ts";
|
|
8
8
|
import type { MqttServerOptions } from "../server/mod.ts";
|
|
9
|
+
import { denoConnToNext } from "./denoConnToNext.ts";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* TLS server that wraps a MqttServer, see the /examples folder
|
|
12
13
|
*/
|
|
13
14
|
export class TlsServer {
|
|
15
|
+
/** the MqttServer instance used by the server */
|
|
16
|
+
readonly mqttServer: MqttServer;
|
|
14
17
|
private listener: Deno.TlsListener;
|
|
15
|
-
private mqttServer: MqttServer;
|
|
16
18
|
/**
|
|
17
19
|
* Create a new TLS server
|
|
18
20
|
*/
|
|
19
21
|
constructor(
|
|
20
22
|
serverOptions: Deno.ListenTlsOptions & Deno.TlsCertifiedKeyPem,
|
|
21
|
-
mqttOptions: MqttServerOptions,
|
|
23
|
+
mqttOptions: MqttServerOptions | MqttServer,
|
|
22
24
|
) {
|
|
23
25
|
this.listener = Deno.listenTls(serverOptions);
|
|
24
|
-
|
|
26
|
+
if (mqttOptions instanceof MqttServer) {
|
|
27
|
+
this.mqttServer = mqttOptions;
|
|
28
|
+
} else {
|
|
29
|
+
this.mqttServer = new MqttServer(mqttOptions);
|
|
30
|
+
}
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
/**
|
|
@@ -29,15 +35,17 @@ export class TlsServer {
|
|
|
29
35
|
*/
|
|
30
36
|
async start(): Promise<void> {
|
|
31
37
|
for await (const conn of this.listener) {
|
|
32
|
-
this.mqttServer.serve(conn);
|
|
38
|
+
this.mqttServer.serve(denoConnToNext(conn));
|
|
33
39
|
}
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
/**
|
|
37
43
|
* Stop listening
|
|
38
44
|
*/
|
|
39
|
-
stop(): void {
|
|
40
|
-
|
|
45
|
+
async stop(opts = { closeMqtt: true }): Promise<void> {
|
|
46
|
+
if (opts.closeMqtt) {
|
|
47
|
+
await this.mqttServer.close();
|
|
48
|
+
}
|
|
41
49
|
this.listener.close();
|
|
42
50
|
}
|
|
43
51
|
/**
|
package/deno/wsServer.test.ts
CHANGED
|
@@ -2,8 +2,9 @@ import { test } from "node:test";
|
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import { WsClient } from "../web/wsClient.ts";
|
|
4
4
|
import { WsServer } from "./wsServer.ts";
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
5
|
+
import { logger, LogLevel } from "../utils/mod.ts";
|
|
6
|
+
import { testClient } from "../dev_utils/testClient.ts";
|
|
7
|
+
import { MqttServer } from "../server/mod.ts";
|
|
7
8
|
|
|
8
9
|
logger.level(LogLevel.info);
|
|
9
10
|
|
|
@@ -25,69 +26,22 @@ test("Deno: Test pubSub using client and server over webSockets", async () => {
|
|
|
25
26
|
});
|
|
26
27
|
|
|
27
28
|
const params = {
|
|
28
|
-
url: new URL(`ws://localhost:${server.port}`),
|
|
29
|
+
url: new URL(`ws://localhost:${server.port}/mqtt`),
|
|
29
30
|
numberOfRetries: 0,
|
|
30
31
|
};
|
|
31
32
|
|
|
32
|
-
logger.verbose("client parameters: ", params);
|
|
33
|
-
|
|
34
33
|
const client = new WsClient();
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
await client.connect(params);
|
|
38
|
-
|
|
39
|
-
const publishSet: { topic: string; qos: QoS }[] = [
|
|
40
|
-
{ topic: "t0@q0", qos: 0 },
|
|
41
|
-
{ topic: "t1@q0", qos: 0 },
|
|
42
|
-
{ topic: "t2@q0", qos: 0 },
|
|
43
|
-
{ topic: "t0@q1", qos: 1 },
|
|
44
|
-
{ topic: "t1@q1", qos: 1 },
|
|
45
|
-
{ topic: "t2@q1", qos: 1 },
|
|
46
|
-
{ topic: "t0@q2", qos: 2 },
|
|
47
|
-
{ topic: "t1@q2", qos: 2 },
|
|
48
|
-
{ topic: "t2@q2", qos: 2 },
|
|
49
|
-
];
|
|
50
|
-
|
|
51
|
-
const subscriptions = publishSet.map((item) => ({
|
|
52
|
-
topicFilter: item.topic,
|
|
53
|
-
qos: item.qos,
|
|
54
|
-
}));
|
|
55
|
-
await client.subscribe({
|
|
56
|
-
subscriptions,
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
// the IIFE ensures message reception runs in parallel
|
|
60
|
-
logger.verbose("Start receiving");
|
|
61
|
-
const received: PublishPacket[] = [];
|
|
62
|
-
(async () => {
|
|
63
|
-
for await (const item of client.messages()) {
|
|
64
|
-
logger.verbose("Receiving:", item.topic, "--", item.qos);
|
|
65
|
-
received.push(item);
|
|
66
|
-
}
|
|
67
|
-
})();
|
|
68
|
-
// end of IIFE
|
|
69
|
-
for (const item of publishSet) {
|
|
70
|
-
logger.verbose("Publishing:", item.topic, "--", item.qos);
|
|
71
|
-
await client.publish({
|
|
72
|
-
topic: item.topic,
|
|
73
|
-
qos: item.qos,
|
|
74
|
-
payload: new Uint8Array([0x01]),
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
await delay(100);
|
|
79
|
-
logger.verbose("Disconnect client");
|
|
80
|
-
await client.disconnect();
|
|
81
|
-
|
|
82
|
-
logger.verbose("Check completeness");
|
|
83
|
-
for (const item of publishSet) {
|
|
84
|
-
const found = received.find((f) =>
|
|
85
|
-
f.topic == item.topic && f.qos === item.qos
|
|
86
|
-
);
|
|
87
|
-
logger.verbose("Found:", item.topic, "--", item.qos);
|
|
88
|
-
assert(found, `${item.topic} -- ${item.qos}`);
|
|
89
|
-
}
|
|
34
|
+
await testClient(client, params);
|
|
90
35
|
|
|
91
36
|
logger.verbose("Stop server");
|
|
92
|
-
server.stop();
|
|
37
|
+
await server.stop();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("Deno: Passing a mqttServer works", () => {
|
|
41
|
+
const mqttServer = new MqttServer({});
|
|
42
|
+
const server = new WsServer(
|
|
43
|
+
{ hostname: "localhost", port: 0 },
|
|
44
|
+
mqttServer,
|
|
45
|
+
);
|
|
46
|
+
assert(server.mqttServer === mqttServer);
|
|
93
47
|
});
|
package/deno/wsServer.ts
CHANGED
|
@@ -5,66 +5,116 @@
|
|
|
5
5
|
import { MqttServer } from "../server/mod.ts";
|
|
6
6
|
import type { MqttServerOptions } from "../server/mod.ts";
|
|
7
7
|
import { logger } from "../utils/logger.ts";
|
|
8
|
-
import {
|
|
9
|
-
import type {
|
|
8
|
+
import { webSocketToNext } from "../web/webSocketToNext.ts";
|
|
9
|
+
import type { NetAddr } from "../connNext/connNext.ts";
|
|
10
|
+
|
|
11
|
+
/** for those who want wrap their own websockets */
|
|
12
|
+
export { webSocketToNext };
|
|
13
|
+
export type RequestHandler = (
|
|
14
|
+
req: Request,
|
|
15
|
+
info: Deno.ServeHandlerInfo<NetAddr>,
|
|
16
|
+
) => Response | Promise<Response>;
|
|
17
|
+
|
|
18
|
+
export const pageNotFound: RequestHandler = (_req, _info) => {
|
|
19
|
+
return new Response("Page not found", { status: 404 });
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
async function handleWsClient(
|
|
23
|
+
socket: WebSocket,
|
|
24
|
+
mqttServer: MqttServer,
|
|
25
|
+
remoteAddr: NetAddr,
|
|
26
|
+
maxBufferSize: number | undefined,
|
|
27
|
+
) {
|
|
28
|
+
const conn = await webSocketToNext(socket, remoteAddr, maxBufferSize);
|
|
29
|
+
mqttServer.serve(conn);
|
|
30
|
+
logger.debug("serving mqtt");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function mqttOverWS(
|
|
34
|
+
mqttServer: MqttServer,
|
|
35
|
+
pathName: string,
|
|
36
|
+
next: RequestHandler,
|
|
37
|
+
opts?: {
|
|
38
|
+
maxBufferSize?: number | undefined;
|
|
39
|
+
},
|
|
40
|
+
): RequestHandler {
|
|
41
|
+
const wsRequestHandler: RequestHandler = (req, info) => {
|
|
42
|
+
const url = new URL(req.url);
|
|
43
|
+
if (url.pathname === pathName) {
|
|
44
|
+
if (req.headers.get("upgrade") !== "websocket") {
|
|
45
|
+
return new Response("Expected WebSocket connection.", { status: 426 });
|
|
46
|
+
}
|
|
47
|
+
logger.debug("starting upgrade to websocket");
|
|
48
|
+
const remoteAddr = info.remoteAddr;
|
|
49
|
+
const { socket, response } = Deno.upgradeWebSocket(req);
|
|
50
|
+
logger.debug("upgraded to websocket");
|
|
51
|
+
// do not await on purpose
|
|
52
|
+
handleWsClient(socket, mqttServer, remoteAddr, opts?.maxBufferSize);
|
|
53
|
+
return response;
|
|
54
|
+
}
|
|
55
|
+
return next(req, info);
|
|
56
|
+
};
|
|
57
|
+
return wsRequestHandler;
|
|
58
|
+
}
|
|
10
59
|
|
|
11
60
|
/**
|
|
12
61
|
* WebSocket server that wraps a MqttServer, see the /examples folder
|
|
13
62
|
*/
|
|
14
63
|
export class WsServer {
|
|
64
|
+
/** the MqttServer instance used by the server */
|
|
65
|
+
readonly mqttServer: MqttServer;
|
|
15
66
|
private server?: Deno.HttpServer;
|
|
16
|
-
private mqttServer: MqttServer;
|
|
17
67
|
private listenOptions: Deno.ServeOptions & {
|
|
18
68
|
port: number;
|
|
19
69
|
hostname?: string;
|
|
20
70
|
};
|
|
71
|
+
private pathName: string;
|
|
72
|
+
private maxBufferSize: number | undefined;
|
|
21
73
|
|
|
22
74
|
/**
|
|
23
75
|
* Create a new WebSocket server
|
|
24
76
|
*/
|
|
25
77
|
constructor(
|
|
26
|
-
serverOptions: Deno.ServeOptions & {
|
|
27
|
-
|
|
78
|
+
serverOptions: Deno.ServeOptions & {
|
|
79
|
+
port: number;
|
|
80
|
+
hostname?: string;
|
|
81
|
+
pathName?: string;
|
|
82
|
+
maxBufferSize?: number;
|
|
83
|
+
},
|
|
84
|
+
mqttOptions: MqttServerOptions | MqttServer,
|
|
28
85
|
) {
|
|
29
|
-
|
|
30
|
-
this.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
86
|
+
const { pathName, maxBufferSize, ...listenOptions } = serverOptions;
|
|
87
|
+
this.listenOptions = listenOptions;
|
|
88
|
+
this.pathName = pathName ?? "/mqtt";
|
|
89
|
+
this.maxBufferSize = maxBufferSize;
|
|
90
|
+
if (mqttOptions instanceof MqttServer) {
|
|
91
|
+
this.mqttServer = mqttOptions;
|
|
92
|
+
} else {
|
|
93
|
+
this.mqttServer = new MqttServer(mqttOptions);
|
|
94
|
+
}
|
|
38
95
|
}
|
|
39
96
|
|
|
40
97
|
/**
|
|
41
98
|
* Start listening
|
|
42
99
|
*/
|
|
43
100
|
async start(): Promise<void> {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return new Response("Expected WebSocket connection.", { status: 426 });
|
|
52
|
-
}
|
|
53
|
-
logger.debug("starting upgrade to websocket");
|
|
54
|
-
const { socket, response } = Deno.upgradeWebSocket(req);
|
|
55
|
-
logger.debug("upgraded to websocket");
|
|
56
|
-
this.handleWsClient(socket, { hostname, port });
|
|
57
|
-
return response;
|
|
58
|
-
});
|
|
59
|
-
|
|
101
|
+
const requestHandler = mqttOverWS(
|
|
102
|
+
this.mqttServer,
|
|
103
|
+
this.pathName,
|
|
104
|
+
pageNotFound,
|
|
105
|
+
{ maxBufferSize: this.maxBufferSize },
|
|
106
|
+
);
|
|
107
|
+
this.server = Deno.serve(this.listenOptions, requestHandler);
|
|
60
108
|
await this.server.finished;
|
|
61
109
|
}
|
|
62
110
|
|
|
63
111
|
/**
|
|
64
112
|
* Stop listening
|
|
65
113
|
*/
|
|
66
|
-
stop(): void {
|
|
67
|
-
|
|
114
|
+
async stop(opts = { closeMqtt: true }): Promise<void> {
|
|
115
|
+
if (opts.closeMqtt) {
|
|
116
|
+
await this.mqttServer.close();
|
|
117
|
+
}
|
|
68
118
|
if (this.server) {
|
|
69
119
|
this.server.shutdown();
|
|
70
120
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duplex AsyncStream
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { AsyncStreamEndpoint } from "./asyncStreamEndpoint.ts";
|
|
6
|
+
/**
|
|
7
|
+
* Internal push/pull queue to support AsyncIterable iteration.
|
|
8
|
+
*/
|
|
9
|
+
class AsyncQueue<T> implements AsyncIterable<T> {
|
|
10
|
+
private queue: T[] = [];
|
|
11
|
+
private resolvers: Array<(value: IteratorResult<T>) => void> = [];
|
|
12
|
+
private closed = false;
|
|
13
|
+
private error: unknown = null;
|
|
14
|
+
|
|
15
|
+
push(value: T): void {
|
|
16
|
+
if (this.closed) return;
|
|
17
|
+
|
|
18
|
+
if (this.resolvers.length > 0) {
|
|
19
|
+
const resolve = this.resolvers.shift()!;
|
|
20
|
+
resolve({ value, done: false });
|
|
21
|
+
} else {
|
|
22
|
+
this.queue.push(value);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
close(): void {
|
|
27
|
+
if (this.closed) return;
|
|
28
|
+
this.closed = true;
|
|
29
|
+
|
|
30
|
+
while (this.resolvers.length > 0) {
|
|
31
|
+
const resolve = this.resolvers.shift()!;
|
|
32
|
+
if (this.error) {
|
|
33
|
+
resolve(Promise.reject(this.error) as unknown as IteratorResult<T>);
|
|
34
|
+
} else {
|
|
35
|
+
resolve({ value: undefined, done: true });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
abort(reason: unknown): void {
|
|
41
|
+
this.error = reason;
|
|
42
|
+
this.close();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async *[Symbol.asyncIterator](): AsyncIterator<T> {
|
|
46
|
+
while (true) {
|
|
47
|
+
if (this.queue.length > 0) {
|
|
48
|
+
yield this.queue.shift()!;
|
|
49
|
+
} else if (this.closed) {
|
|
50
|
+
if (this.error) {
|
|
51
|
+
throw this.error;
|
|
52
|
+
}
|
|
53
|
+
return;
|
|
54
|
+
} else {
|
|
55
|
+
const nextResult = await new Promise<IteratorResult<T>>((resolve) => {
|
|
56
|
+
this.resolvers.push(resolve);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (nextResult.done) {
|
|
60
|
+
if (this.error) {
|
|
61
|
+
throw this.error;
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
yield nextResult.value;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function createAsyncStreamPair(): {
|
|
73
|
+
input: AsyncStreamEndpoint;
|
|
74
|
+
output: AsyncStreamEndpoint;
|
|
75
|
+
} {
|
|
76
|
+
const queueA = new AsyncQueue<Uint8Array>();
|
|
77
|
+
const queueB = new AsyncQueue<Uint8Array>();
|
|
78
|
+
|
|
79
|
+
let closed = false;
|
|
80
|
+
|
|
81
|
+
const closeBoth = () => {
|
|
82
|
+
if (!closed) {
|
|
83
|
+
closed = true;
|
|
84
|
+
queueA.close();
|
|
85
|
+
queueB.close();
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
input: {
|
|
91
|
+
readable: queueA,
|
|
92
|
+
write: (chunk: Uint8Array): Promise<void> => {
|
|
93
|
+
if (closed) throw new Error("Stream is closed");
|
|
94
|
+
queueB.push(chunk);
|
|
95
|
+
return Promise.resolve();
|
|
96
|
+
},
|
|
97
|
+
close: closeBoth,
|
|
98
|
+
},
|
|
99
|
+
output: {
|
|
100
|
+
readable: queueB,
|
|
101
|
+
write: (chunk: Uint8Array): Promise<void> => {
|
|
102
|
+
if (closed) throw new Error("Stream is closed");
|
|
103
|
+
queueA.push(chunk);
|
|
104
|
+
return Promise.resolve();
|
|
105
|
+
},
|
|
106
|
+
close: closeBoth,
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|