aedes 0.51.3 → 1.0.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/.claude/settings.local.json +12 -0
- package/.github/actions/sticky-pr-comment/action.yml +55 -0
- package/.github/workflows/benchmark-compare-serial.yml +60 -0
- package/.github/workflows/ci.yml +12 -17
- package/.release-it.json +18 -0
- package/.taprc +15 -6
- package/README.md +6 -4
- package/aedes.d.ts +0 -6
- package/aedes.js +270 -242
- package/benchmarks/README.md +33 -0
- package/benchmarks/pingpong.js +94 -25
- package/benchmarks/receiver.js +77 -0
- package/benchmarks/report.js +150 -0
- package/benchmarks/runBenchmarks.js +118 -0
- package/benchmarks/sender.js +86 -0
- package/benchmarks/server.js +19 -18
- package/checkVersion.js +20 -0
- package/docs/Aedes.md +66 -8
- package/docs/Client.md +3 -4
- package/docs/Examples.md +39 -22
- package/docs/MIGRATION.md +50 -0
- package/eslint.config.js +8 -0
- package/example.js +51 -40
- package/examples/clusters/index.js +28 -23
- package/examples/clusters/package.json +10 -6
- package/lib/client.js +405 -306
- package/lib/handlers/connect.js +42 -38
- package/lib/handlers/index.js +9 -11
- package/lib/handlers/ping.js +2 -3
- package/lib/handlers/puback.js +5 -5
- package/lib/handlers/publish.js +29 -14
- package/lib/handlers/pubrec.js +9 -17
- package/lib/handlers/pubrel.js +34 -25
- package/lib/handlers/subscribe.js +47 -43
- package/lib/handlers/unsubscribe.js +16 -19
- package/lib/qos-packet.js +14 -17
- package/lib/utils.js +5 -12
- package/lib/write.js +4 -5
- package/package.json +139 -136
- package/test/auth.js +468 -804
- package/test/basic.js +613 -575
- package/test/bridge.js +44 -40
- package/test/client-pub-sub.js +531 -504
- package/test/close_socket_by_other_party.js +137 -102
- package/test/connect.js +487 -484
- package/test/drain-timeout.js +593 -0
- package/test/drain-toxiproxy.js +620 -0
- package/test/events.js +173 -145
- package/test/helper.js +351 -73
- package/test/keep-alive.js +40 -67
- package/test/meta.js +257 -210
- package/test/not-blocking.js +93 -197
- package/test/qos1.js +464 -554
- package/test/qos2.js +308 -393
- package/test/regr-21.js +39 -21
- package/test/require.cjs +22 -0
- package/test/retain.js +349 -398
- package/test/topics.js +176 -183
- package/test/types/aedes.test-d.ts +4 -8
- package/test/will.js +310 -428
- package/types/instance.d.ts +40 -35
- package/types/packet.d.ts +10 -10
- package/.coveralls.yml +0 -1
- package/benchmarks/bombing.js +0 -34
- package/benchmarks/bombingQoS1.js +0 -36
- package/benchmarks/throughputCounter.js +0 -23
- package/benchmarks/throughputCounterQoS1.js +0 -33
- package/types/.eslintrc.json +0 -47
package/test/events.js
CHANGED
|
@@ -1,27 +1,46 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import { createServer } from 'node:net'
|
|
3
|
+
import { once } from 'node:events'
|
|
4
|
+
import mqemitter from 'mqemitter'
|
|
5
|
+
import {
|
|
6
|
+
brokerPublish,
|
|
7
|
+
connect,
|
|
8
|
+
createAndConnect,
|
|
9
|
+
delay,
|
|
10
|
+
nextPacketWithTimeOut,
|
|
11
|
+
setup,
|
|
12
|
+
subscribe,
|
|
13
|
+
withTimeout
|
|
14
|
+
} from './helper.js'
|
|
15
|
+
import { Aedes } from '../aedes.js'
|
|
16
|
+
import mqtt from 'mqtt'
|
|
17
|
+
|
|
18
|
+
async function brokerClose (broker) {
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
broker.close(resolve)
|
|
21
|
+
})
|
|
22
|
+
}
|
|
7
23
|
|
|
8
|
-
test('publishes an hearbeat',
|
|
24
|
+
test('publishes an hearbeat', async (t) => {
|
|
9
25
|
t.plan(2)
|
|
10
26
|
|
|
11
|
-
const broker =
|
|
27
|
+
const broker = await Aedes.createBroker({
|
|
12
28
|
heartbeatInterval: 10 // ms
|
|
13
29
|
})
|
|
14
|
-
t.
|
|
30
|
+
t.after(() => broker.close())
|
|
15
31
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
32
|
+
await new Promise((resolve) => {
|
|
33
|
+
broker.subscribe('$SYS/+/heartbeat', (message, cb) => {
|
|
34
|
+
const id = message.topic.match(/\$SYS\/([^/]+)\/heartbeat/)[1]
|
|
35
|
+
t.assert.equal(id, broker.id, 'broker id matches')
|
|
36
|
+
t.assert.deepEqual(message.payload.toString(), id, 'message has id as the payload')
|
|
37
|
+
cb()
|
|
38
|
+
resolve()
|
|
39
|
+
})
|
|
21
40
|
})
|
|
22
41
|
})
|
|
23
42
|
|
|
24
|
-
test('publishes birth',
|
|
43
|
+
test('publishes birth', async (t) => {
|
|
25
44
|
t.plan(4)
|
|
26
45
|
|
|
27
46
|
const mq = mqemitter()
|
|
@@ -30,161 +49,167 @@ test('publishes birth', function (t) {
|
|
|
30
49
|
const clientId = 'test-client'
|
|
31
50
|
|
|
32
51
|
mq.on(`$SYS/${brokerId}/birth`, (message, cb) => {
|
|
33
|
-
t.
|
|
34
|
-
t.
|
|
52
|
+
t.assert.ok(true, 'broker birth received')
|
|
53
|
+
t.assert.deepEqual(message.payload.toString(), brokerId, 'message has id as the payload')
|
|
35
54
|
cb()
|
|
36
55
|
})
|
|
37
56
|
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
57
|
+
const s = await createAndConnect(t, {
|
|
58
|
+
broker: {
|
|
59
|
+
id: brokerId,
|
|
60
|
+
mq
|
|
61
|
+
},
|
|
62
|
+
connect: {
|
|
63
|
+
clientId
|
|
64
|
+
}
|
|
41
65
|
})
|
|
42
66
|
|
|
43
|
-
|
|
44
|
-
|
|
67
|
+
t.assert.equal(s.client.id, clientId, 'client connected')
|
|
68
|
+
|
|
69
|
+
await new Promise(resolve => {
|
|
45
70
|
// set a fake counter on a fake broker
|
|
46
71
|
process.nextTick(() => {
|
|
47
|
-
broker.clients[clientId].duplicates[fakeBroker] = 42
|
|
72
|
+
s.broker.clients[clientId].duplicates[fakeBroker] = 42
|
|
48
73
|
mq.emit({ topic: `$SYS/${fakeBroker}/birth`, payload: Buffer.from(fakeBroker) })
|
|
49
74
|
})
|
|
50
|
-
})
|
|
51
75
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
76
|
+
mq.on(`$SYS/${fakeBroker}/birth`, (message, cb) => {
|
|
77
|
+
process.nextTick(() => {
|
|
78
|
+
t.assert.equal(!!s.broker.clients[clientId].duplicates[fakeBroker], false, 'client duplicates has been resetted')
|
|
79
|
+
resolve()
|
|
80
|
+
cb()
|
|
81
|
+
})
|
|
56
82
|
})
|
|
57
83
|
})
|
|
58
|
-
|
|
59
|
-
const s = connect(setup(broker), { clientId })
|
|
60
|
-
t.teardown(s.broker.close.bind(s.broker))
|
|
61
84
|
})
|
|
62
85
|
|
|
63
|
-
|
|
64
|
-
test(
|
|
65
|
-
t.plan(
|
|
86
|
+
for (const topic of ['$mcollina', '$SYS']) {
|
|
87
|
+
test(`does not forward $ prefixed topics to # subscription - ${topic}`, async (t) => {
|
|
88
|
+
t.plan(5)
|
|
66
89
|
|
|
67
|
-
const s =
|
|
68
|
-
t
|
|
69
|
-
|
|
70
|
-
subscribe(t, s, '#', 0, function () {
|
|
71
|
-
s.outStream.once('data', function (packet) {
|
|
72
|
-
t.fail('no packet should be received')
|
|
73
|
-
})
|
|
90
|
+
const s = await createAndConnect(t)
|
|
91
|
+
await subscribe(t, s, '#', 0)
|
|
74
92
|
|
|
93
|
+
await new Promise(resolve => {
|
|
75
94
|
s.broker.mq.emit({
|
|
76
95
|
cmd: 'publish',
|
|
77
96
|
topic: topic + '/hello',
|
|
78
97
|
payload: 'world'
|
|
79
|
-
},
|
|
80
|
-
t.
|
|
98
|
+
}, () => {
|
|
99
|
+
t.assert.ok(true, 'nothing happened')
|
|
100
|
+
resolve()
|
|
81
101
|
})
|
|
82
102
|
})
|
|
103
|
+
const packet = await nextPacketWithTimeOut(s, 10)
|
|
104
|
+
t.assert.equal(packet, null, 'no packet should be received')
|
|
83
105
|
})
|
|
84
106
|
|
|
85
|
-
test('does not forward $ prefixed topics to +/# subscription - ' + topic,
|
|
86
|
-
t.plan(
|
|
87
|
-
|
|
88
|
-
const s = connect(setup())
|
|
89
|
-
t.teardown(s.broker.close.bind(s.broker))
|
|
107
|
+
test('does not forward $ prefixed topics to +/# subscription - ' + topic, async (t) => {
|
|
108
|
+
t.plan(5)
|
|
90
109
|
|
|
91
|
-
|
|
92
|
-
s.outStream.once('data', function (packet) {
|
|
93
|
-
t.fail('no packet should be received')
|
|
94
|
-
})
|
|
110
|
+
const s = await createAndConnect(t)
|
|
95
111
|
|
|
112
|
+
await subscribe(t, s, '+/#', 0)
|
|
113
|
+
await new Promise(resolve => {
|
|
96
114
|
s.broker.mq.emit({
|
|
97
115
|
cmd: 'publish',
|
|
98
116
|
topic: topic + '/hello',
|
|
99
117
|
payload: 'world'
|
|
100
|
-
},
|
|
101
|
-
t.
|
|
118
|
+
}, () => {
|
|
119
|
+
t.assert.ok(true, 'nothing happened')
|
|
120
|
+
resolve()
|
|
102
121
|
})
|
|
103
122
|
})
|
|
123
|
+
const packet = await nextPacketWithTimeOut(s, 10)
|
|
124
|
+
t.assert.equal(packet, null, 'no packet should be received')
|
|
104
125
|
})
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
test('does not store $SYS topics to QoS 1 # subscription', function (t) {
|
|
108
|
-
t.plan(3)
|
|
126
|
+
}
|
|
109
127
|
|
|
110
|
-
|
|
111
|
-
t.
|
|
128
|
+
test('does not store $SYS topics to QoS 1 # subscription', async (t) => {
|
|
129
|
+
t.plan(4)
|
|
112
130
|
|
|
113
|
-
const opts = { clean: false, clientId: 'abcde' }
|
|
114
|
-
|
|
131
|
+
const opts = { connect: { clean: false, clientId: 'abcde' } }
|
|
132
|
+
const s1 = await createAndConnect(t, opts)
|
|
115
133
|
|
|
116
|
-
subscribe(t,
|
|
117
|
-
|
|
134
|
+
await subscribe(t, s1, '#', 1)
|
|
135
|
+
s1.inStream.end()
|
|
118
136
|
|
|
119
|
-
|
|
120
|
-
cmd: 'publish',
|
|
121
|
-
topic: '$SYS/hello',
|
|
122
|
-
payload: 'world',
|
|
123
|
-
qos: 1
|
|
124
|
-
}, function () {
|
|
125
|
-
s = connect(setup(broker), { clean: false, clientId: 'abcde' })
|
|
137
|
+
await once(s1.conn, 'close')
|
|
126
138
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
139
|
+
await brokerPublish(s1, {
|
|
140
|
+
cmd: 'publish',
|
|
141
|
+
topic: '$SYS/hello',
|
|
142
|
+
payload: 'world',
|
|
143
|
+
qos: 1
|
|
131
144
|
})
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
test('Emit event when receives a ping', { timeout: 2000 }, function (t) {
|
|
135
|
-
t.plan(5)
|
|
136
145
|
|
|
137
|
-
const
|
|
138
|
-
|
|
146
|
+
const s2 = setup(s1.broker)
|
|
147
|
+
await connect(s2, opts)
|
|
139
148
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
t.equal(packet.cmd, 'pingreq')
|
|
144
|
-
t.equal(packet.payload, null)
|
|
145
|
-
t.equal(packet.topic, null)
|
|
146
|
-
t.equal(packet.length, 0)
|
|
147
|
-
}
|
|
148
|
-
})
|
|
149
|
+
const packet = await nextPacketWithTimeOut(s2, 10)
|
|
150
|
+
t.assert.equal(packet, null, 'no packet should be received from client 2')
|
|
151
|
+
})
|
|
149
152
|
|
|
150
|
-
|
|
153
|
+
test('Emit event when receives a ping', async (t) => {
|
|
154
|
+
t.plan(5)
|
|
151
155
|
|
|
156
|
+
const clientId = 'abcde'
|
|
157
|
+
const s = await createAndConnect(t, { connect: { keepalive: 1, clientId } })
|
|
158
|
+
await delay(1)
|
|
152
159
|
s.inStream.write({
|
|
153
160
|
cmd: 'pingreq'
|
|
154
161
|
})
|
|
162
|
+
|
|
163
|
+
const [packet, client] = await once(s.broker, 'ping')
|
|
164
|
+
|
|
165
|
+
t.assert.equal(client?.id, clientId)
|
|
166
|
+
t.assert.equal(packet?.cmd, 'pingreq')
|
|
167
|
+
t.assert.equal(packet?.payload, null)
|
|
168
|
+
t.assert.equal(packet?.topic, null)
|
|
169
|
+
t.assert.equal(packet?.length, 0)
|
|
155
170
|
})
|
|
156
171
|
|
|
157
|
-
test('Emit event when broker closed',
|
|
172
|
+
test('Emit event when broker closed', async (t) => {
|
|
158
173
|
t.plan(1)
|
|
159
174
|
|
|
160
|
-
const broker =
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
175
|
+
const broker = await Aedes.createBroker()
|
|
176
|
+
// run parallel
|
|
177
|
+
await Promise.all([
|
|
178
|
+
once(broker, 'closed'),
|
|
179
|
+
brokerClose(broker)
|
|
180
|
+
])
|
|
181
|
+
t.assert.ok(true, 'closed event fired')
|
|
165
182
|
})
|
|
166
183
|
|
|
167
|
-
test('Emit closed event
|
|
184
|
+
test('Emit closed event only once when double broker.close()', async (t) => {
|
|
168
185
|
t.plan(4)
|
|
169
186
|
|
|
170
|
-
const broker =
|
|
171
|
-
broker.
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
187
|
+
const broker = await Aedes.createBroker()
|
|
188
|
+
t.assert.ok(!broker.closed, 'broker not closed')
|
|
189
|
+
|
|
190
|
+
const closedEvent = async (expected) => {
|
|
191
|
+
const [result] = await withTimeout(once(broker, 'closed'), 10, ['timeout'])
|
|
192
|
+
t.assert.equal(result, expected, `closed event ${expected || ''}`)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
await Promise.all([
|
|
196
|
+
closedEvent(undefined),
|
|
197
|
+
broker.close()
|
|
198
|
+
])
|
|
199
|
+
t.assert.ok(broker.closed, 'broker closed')
|
|
200
|
+
await Promise.all([
|
|
201
|
+
closedEvent('timeout'),
|
|
202
|
+
broker.close()
|
|
203
|
+
])
|
|
179
204
|
})
|
|
180
205
|
|
|
181
|
-
test('Test backpressure aedes published function',
|
|
206
|
+
test('Test backpressure aedes published function', async (t) => {
|
|
182
207
|
t.plan(2)
|
|
183
208
|
|
|
184
209
|
let publishCount = 10
|
|
185
210
|
let count = 0
|
|
186
|
-
|
|
187
|
-
const broker =
|
|
211
|
+
let publisher
|
|
212
|
+
const broker = await Aedes.createBroker({
|
|
188
213
|
published: function (packet, client, done) {
|
|
189
214
|
if (client) {
|
|
190
215
|
count++
|
|
@@ -195,48 +220,51 @@ test('Test backpressure aedes published function', function (t) {
|
|
|
195
220
|
} else { done() }
|
|
196
221
|
}
|
|
197
222
|
})
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
223
|
+
t.after(() => {
|
|
224
|
+
broker.close()
|
|
225
|
+
server.close()
|
|
226
|
+
})
|
|
227
|
+
const server = createServer(broker.handle)
|
|
228
|
+
|
|
229
|
+
await new Promise(resolve => {
|
|
230
|
+
server.listen(0, () => {
|
|
231
|
+
const port = server.address().port
|
|
232
|
+
publisher = mqtt.connect({ port, host: 'localhost', clean: true, keepalive: 30 })
|
|
233
|
+
|
|
234
|
+
function next () {
|
|
235
|
+
if (--publishCount > 0) { process.nextTick(publish) }
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function publish () {
|
|
239
|
+
publisher.publish('test', 'payload', next)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
publisher.on('connect', publish)
|
|
243
|
+
publisher.on('end', () => {
|
|
244
|
+
t.assert.ok(count > publishCount)
|
|
245
|
+
t.assert.equal(publishCount, 0)
|
|
246
|
+
resolve()
|
|
247
|
+
})
|
|
221
248
|
})
|
|
222
249
|
})
|
|
223
250
|
})
|
|
224
251
|
|
|
225
|
-
test('clear closed clients when the same clientId is managed by another broker',
|
|
252
|
+
test('clear closed clients when the same clientId is managed by another broker', async (t) => {
|
|
226
253
|
t.plan(2)
|
|
227
254
|
|
|
228
255
|
const clientId = 'closed-client'
|
|
229
|
-
const
|
|
230
|
-
|
|
256
|
+
const broker = await Aedes.createBroker()
|
|
257
|
+
t.after(() => broker.close())
|
|
231
258
|
// simulate a closed client on the broker
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
259
|
+
broker.clients[clientId] = { closed: true, broker }
|
|
260
|
+
broker.connectedClients = 1
|
|
261
|
+
|
|
262
|
+
await new Promise(resolve => {
|
|
263
|
+
// simulate the creation of the same client on another broker of the cluster
|
|
264
|
+
broker.publish({ topic: '$SYS/anotherbroker/new/clients', payload: clientId }, () => {
|
|
265
|
+
t.assert.equal(broker.clients[clientId], undefined) // check that the closed client was removed
|
|
266
|
+
t.assert.equal(broker.connectedClients, 0)
|
|
267
|
+
resolve()
|
|
268
|
+
})
|
|
239
269
|
})
|
|
240
|
-
|
|
241
|
-
t.teardown(aedesBroker.close.bind(aedesBroker))
|
|
242
270
|
})
|