aedes 0.51.2 → 1.0.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 (67) hide show
  1. package/.github/actions/sticky-pr-comment/action.yml +55 -0
  2. package/.github/workflows/benchmark-compare-serial.yml +60 -0
  3. package/.github/workflows/ci.yml +12 -17
  4. package/.release-it.json +18 -0
  5. package/.taprc +15 -6
  6. package/README.md +6 -4
  7. package/aedes.d.ts +0 -6
  8. package/aedes.js +270 -238
  9. package/benchmarks/README.md +33 -0
  10. package/benchmarks/pingpong.js +94 -25
  11. package/benchmarks/receiver.js +77 -0
  12. package/benchmarks/report.js +150 -0
  13. package/benchmarks/runBenchmarks.js +118 -0
  14. package/benchmarks/sender.js +86 -0
  15. package/benchmarks/server.js +19 -18
  16. package/checkVersion.js +20 -0
  17. package/docs/Aedes.md +66 -8
  18. package/docs/Client.md +3 -4
  19. package/docs/Examples.md +39 -22
  20. package/docs/MIGRATION.md +50 -0
  21. package/eslint.config.js +8 -0
  22. package/example.js +51 -40
  23. package/examples/clusters/index.js +28 -23
  24. package/examples/clusters/package.json +10 -6
  25. package/lib/client.js +405 -306
  26. package/lib/handlers/connect.js +42 -38
  27. package/lib/handlers/index.js +9 -11
  28. package/lib/handlers/ping.js +2 -3
  29. package/lib/handlers/puback.js +5 -5
  30. package/lib/handlers/publish.js +29 -14
  31. package/lib/handlers/pubrec.js +9 -17
  32. package/lib/handlers/pubrel.js +34 -25
  33. package/lib/handlers/subscribe.js +54 -43
  34. package/lib/handlers/unsubscribe.js +16 -19
  35. package/lib/qos-packet.js +14 -17
  36. package/lib/utils.js +5 -12
  37. package/lib/write.js +4 -5
  38. package/package.json +134 -136
  39. package/test/auth.js +468 -804
  40. package/test/basic.js +613 -575
  41. package/test/bridge.js +44 -40
  42. package/test/client-pub-sub.js +531 -504
  43. package/test/close_socket_by_other_party.js +137 -102
  44. package/test/connect.js +487 -484
  45. package/test/drain-timeout.js +593 -0
  46. package/test/drain-toxiproxy.js +620 -0
  47. package/test/events.js +174 -144
  48. package/test/helper.js +351 -73
  49. package/test/keep-alive.js +40 -67
  50. package/test/meta.js +257 -210
  51. package/test/not-blocking.js +93 -197
  52. package/test/qos1.js +464 -554
  53. package/test/qos2.js +308 -393
  54. package/test/regr-21.js +39 -21
  55. package/test/require.cjs +22 -0
  56. package/test/retain.js +349 -398
  57. package/test/topics.js +176 -183
  58. package/test/types/aedes.test-d.ts +4 -8
  59. package/test/will.js +310 -428
  60. package/types/instance.d.ts +40 -35
  61. package/types/packet.d.ts +10 -10
  62. package/.coveralls.yml +0 -1
  63. package/benchmarks/bombing.js +0 -34
  64. package/benchmarks/bombingQoS1.js +0 -36
  65. package/benchmarks/throughputCounter.js +0 -23
  66. package/benchmarks/throughputCounterQoS1.js +0 -33
  67. package/types/.eslintrc.json +0 -47
package/test/meta.js CHANGED
@@ -1,318 +1,360 @@
1
- 'use strict'
2
-
3
- const { test } = require('tap')
4
- const { setup, connect, subscribe, noError } = require('./helper')
5
- const aedes = require('../')
6
-
7
- test('count connected clients', function (t) {
1
+ import { test } from 'node:test'
2
+ import { once } from 'node:events'
3
+ import {
4
+ connect,
5
+ createAndConnect,
6
+ delay,
7
+ nextPacket,
8
+ setup,
9
+ subscribe,
10
+ withTimeout
11
+ } from './helper.js'
12
+ import { Aedes } from '../aedes.js'
13
+ import pkg from '../package.json' with { type: 'json' }
14
+ const version = pkg.version
15
+
16
+ test('count connected clients', async (t) => {
8
17
  t.plan(4)
9
18
 
10
- const broker = aedes()
11
- t.teardown(broker.close.bind(broker))
12
-
13
- t.equal(broker.connectedClients, 0, 'no connected clients')
19
+ const broker = await Aedes.createBroker()
20
+ t.after(() => broker.close())
21
+ t.assert.equal(broker.connectedClients, 0, 'no connected clients')
14
22
 
15
- connect(setup(broker), {
16
- }, function () {
17
- t.equal(broker.connectedClients, 1, 'one connected clients')
23
+ await connect(setup(broker), { autoClientId: true })
24
+ t.assert.equal(broker.connectedClients, 1, 'one connected clients')
18
25
 
19
- const last = connect(setup(broker), {
20
- }, function () {
21
- t.equal(broker.connectedClients, 2, 'two connected clients')
26
+ const last = setup(broker)
27
+ await connect(last, { autoClientId: true })
28
+ t.assert.equal(broker.connectedClients, 2, 'two connected clients')
22
29
 
23
- last.conn.destroy()
30
+ last.conn.destroy()
24
31
 
25
- // needed because destroy() will do the trick before
26
- // the next tick
27
- setImmediate(function () {
28
- t.equal(broker.connectedClients, 1, 'one connected clients')
29
- })
30
- })
31
- })
32
+ // needed because destroy() will do the trick before
33
+ // the next tick
34
+ await delay(0)
35
+ t.assert.equal(broker.connectedClients, 1, 'one connected clients')
32
36
  })
33
37
 
34
- test('call published method', function (t) {
38
+ test('call published method', async (t) => {
35
39
  t.plan(4)
36
40
 
37
- const broker = aedes()
38
- t.teardown(broker.close.bind(broker))
41
+ const broker = await Aedes.createBroker()
42
+ t.after(() => broker.close())
39
43
 
40
- broker.published = function (packet, client, done) {
41
- t.equal(packet.topic, 'hello', 'topic matches')
42
- t.equal(packet.payload.toString(), 'world', 'payload matches')
43
- t.equal(client, null, 'no client')
44
+ broker.published = (packet, client, done) => {
45
+ t.assert.equal(packet.topic, 'hello', 'topic matches')
46
+ t.assert.equal(packet.payload.toString(), 'world', 'payload matches')
47
+ t.assert.equal(client, null, 'no client')
44
48
  done()
45
49
  }
46
50
 
47
- broker.publish({
48
- topic: 'hello',
49
- payload: Buffer.from('world')
50
- }, function (err) {
51
- t.error(err, 'no error')
51
+ await new Promise((resolve) => {
52
+ broker.publish({
53
+ topic: 'hello',
54
+ payload: Buffer.from('world')
55
+ }, err => {
56
+ t.assert.ok(!err, 'no error')
57
+ resolve()
58
+ })
52
59
  })
53
60
  })
54
61
 
55
- test('call published method with client', function (t) {
62
+ test('call published method with client', async (t) => {
56
63
  t.plan(4)
57
64
 
58
- const broker = aedes()
59
- t.teardown(broker.close.bind(broker))
60
-
61
- broker.published = function (packet, client, done) {
62
- // for internal messages, client will be null
63
- if (client) {
64
- t.equal(packet.topic, 'hello', 'topic matches')
65
- t.equal(packet.payload.toString(), 'world', 'payload matches')
66
- t.equal(packet.qos, 1)
67
- t.equal(packet.messageId, 42)
68
- done()
65
+ const s = await createAndConnect(t)
66
+
67
+ const checkPublished = new Promise(resolve => {
68
+ s.broker.published = (packet, client, done) => {
69
+ // for internal messages, client will be null
70
+ if (client) {
71
+ t.assert.equal(packet.topic, 'hello', 'topic matches')
72
+ t.assert.equal(packet.payload.toString(), 'world', 'payload matches')
73
+ t.assert.equal(packet.qos, 1)
74
+ t.assert.equal(packet.messageId, 42)
75
+ done()
76
+ resolve()
77
+ }
69
78
  }
70
- }
71
-
72
- const s = connect(setup(broker))
73
-
74
- s.inStream.write({
75
- cmd: 'publish',
76
- topic: 'hello',
77
- payload: Buffer.from('world'),
78
- qos: 1,
79
- messageId: 42
80
79
  })
80
+
81
+ const sendPacket = () => {
82
+ s.inStream.write({
83
+ cmd: 'publish',
84
+ topic: 'hello',
85
+ payload: Buffer.from('world'),
86
+ qos: 1,
87
+ messageId: 42
88
+ })
89
+ }
90
+ // run parallel
91
+ await Promise.all([checkPublished, sendPacket()])
81
92
  })
82
93
 
83
- test('emit publish event with client - QoS 0', function (t) {
84
- t.plan(3)
94
+ test('emit publish event with client - QoS 0', async (t) => {
95
+ t.plan(4)
85
96
 
86
- const broker = aedes()
87
- t.teardown(broker.close.bind(broker))
97
+ const s = await createAndConnect(t)
88
98
 
89
- broker.on('publish', function (packet, client) {
99
+ const checkPublished = async () => {
100
+ const [packet, client] = await once(s.broker, 'publish')
90
101
  // for internal messages, client will be null
91
- if (client) {
92
- t.equal(packet.qos, 0)
93
- t.equal(packet.topic, 'hello', 'topic matches')
94
- t.equal(packet.payload.toString(), 'world', 'payload matches')
95
- }
96
- })
97
-
98
- const s = connect(setup(broker))
102
+ t.assert.ok(client, 'client is present')
103
+ t.assert.equal(packet.qos, 0)
104
+ t.assert.equal(packet.topic, 'hello', 'topic matches')
105
+ t.assert.equal(packet.payload.toString(), 'world', 'payload matches')
106
+ }
99
107
 
100
- s.inStream.write({
101
- cmd: 'publish',
102
- topic: 'hello',
103
- payload: Buffer.from('world'),
104
- qos: 0
105
- })
108
+ const sendPacket = () => {
109
+ s.inStream.write({
110
+ cmd: 'publish',
111
+ topic: 'hello',
112
+ payload: Buffer.from('world'),
113
+ qos: 0
114
+ })
115
+ }
116
+ await Promise.all([checkPublished(), sendPacket()])
106
117
  })
107
118
 
108
- test('emit publish event with client - QoS 1', function (t) {
109
- t.plan(4)
119
+ test('emit publish event with client - QoS 1', async (t) => {
120
+ t.plan(5)
110
121
 
111
- const broker = aedes()
112
- t.teardown(broker.close.bind(broker))
122
+ const s = await createAndConnect(t)
113
123
 
114
- broker.on('publish', function (packet, client) {
124
+ const checkPublished = async () => {
125
+ const [packet, client] = await once(s.broker, 'publish')
115
126
  // for internal messages, client will be null
116
- if (client) {
117
- t.equal(packet.qos, 1)
118
- t.equal(packet.messageId, 42)
119
- t.equal(packet.topic, 'hello', 'topic matches')
120
- t.equal(packet.payload.toString(), 'world', 'payload matches')
121
- }
122
- })
123
-
124
- const s = connect(setup(broker))
127
+ t.assert.ok(client, 'client is present')
128
+ t.assert.equal(packet.messageId, 42)
129
+ t.assert.equal(packet.qos, 1)
130
+ t.assert.equal(packet.topic, 'hello', 'topic matches')
131
+ t.assert.equal(packet.payload.toString(), 'world', 'payload matches')
132
+ }
125
133
 
126
- s.inStream.write({
127
- cmd: 'publish',
128
- topic: 'hello',
129
- payload: Buffer.from('world'),
130
- qos: 1,
131
- messageId: 42
132
- })
134
+ const sendPacket = () => {
135
+ s.inStream.write({
136
+ cmd: 'publish',
137
+ topic: 'hello',
138
+ payload: Buffer.from('world'),
139
+ qos: 1,
140
+ messageId: 42
141
+ })
142
+ }
143
+ await Promise.all([checkPublished(), sendPacket()])
133
144
  })
134
145
 
135
- test('emit subscribe event', function (t) {
146
+ test('emit subscribe event', async (t) => {
136
147
  t.plan(6)
137
148
 
138
- const broker = aedes()
139
- t.teardown(broker.close.bind(broker))
140
-
141
- const s = connect(setup(broker), { clientId: 'abcde' })
149
+ const s = await createAndConnect(t, { connect: { clientId: 'abcde' } })
142
150
 
143
- broker.on('subscribe', function (subscriptions, client) {
144
- t.same(subscriptions, [{
151
+ const checkSubscribe = async () => {
152
+ const [subscriptions, client] = await once(s.broker, 'subscribe')
153
+ t.assert.deepEqual(subscriptions, [{
145
154
  topic: 'hello',
146
155
  qos: 0
147
156
  }], 'topic matches')
148
- t.equal(client.id, 'abcde', 'client matches')
149
- })
157
+ t.assert.equal(subscriptions[0].qos, 0)
158
+ t.assert.equal(client.id, 'abcde', 'client matches')
159
+ }
150
160
 
151
- subscribe(t, s, 'hello', 0, function () {
152
- t.pass('subscribe completed')
153
- })
161
+ // run parallel
162
+ await Promise.all([
163
+ checkSubscribe(),
164
+ subscribe(t, s, 'hello', 0)
165
+ ])
154
166
  })
155
167
 
156
- test('emit subscribe event if unrecognized params in subscribe packet structure', function (t) {
168
+ test('emit subscribe event if unrecognized params in subscribe packet structure', async (t) => {
157
169
  t.plan(3)
158
170
 
159
- const broker = aedes()
160
- t.teardown(broker.close.bind(broker))
171
+ const s = await createAndConnect(t)
161
172
 
162
- const s = noError(connect(setup(broker)))
163
173
  const subs = [{ topic: 'hello', qos: 0 }]
164
174
 
165
- broker.on('subscribe', function (subscriptions, client) {
166
- t.equal(subscriptions, subs)
167
- t.same(client, s.client)
168
- })
175
+ const checkSubscribe = async () => {
176
+ const [subscriptions, client] = await once(s.broker, 'subscribe')
177
+ t.assert.equal(subscriptions, subs)
178
+ t.assert.deepEqual(client, s.client)
179
+ }
169
180
 
170
- s.client.subscribe({
171
- subscriptions: subs,
172
- restore: true
173
- }, function (err) {
174
- t.error(err)
181
+ const doSubscribe = new Promise((resolve) => {
182
+ s.client.subscribe({
183
+ subscriptions: subs,
184
+ close: true
185
+ }, err => {
186
+ t.assert.ok(!err)
187
+ resolve()
188
+ })
175
189
  })
176
- })
177
190
 
178
- test('emit unsubscribe event', function (t) {
179
- t.plan(6)
191
+ // run parallel
192
+ await Promise.all([
193
+ checkSubscribe(),
194
+ doSubscribe
195
+ ])
196
+ })
180
197
 
181
- const broker = aedes()
182
- t.teardown(broker.close.bind(broker))
198
+ test('emit unsubscribe event', async (t) => {
199
+ t.plan(5)
183
200
 
184
- const s = connect(setup(broker), { clean: true, clientId: 'abcde' })
201
+ const s = await createAndConnect(t, { connect: { clean: true, clientId: 'abcde' } })
185
202
 
186
- broker.on('unsubscribe', function (unsubscriptions, client) {
187
- t.same(unsubscriptions, [
203
+ const checkUnsubscribe = async () => {
204
+ const [unsubscriptions, client] = await once(s.broker, 'unsubscribe')
205
+ t.assert.deepEqual(unsubscriptions, [
188
206
  'hello'
189
207
  ], 'unsubscription matches')
190
- t.equal(client.id, 'abcde', 'client matches')
191
- })
208
+ t.assert.equal(client.id, 'abcde', 'client matches')
209
+ }
192
210
 
193
- subscribe(t, s, 'hello', 0, function () {
211
+ const doUnsubscribe = async () => {
212
+ await subscribe(t, s, 'hello', 0)
194
213
  s.inStream.write({
195
214
  cmd: 'unsubscribe',
196
215
  messageId: 43,
197
216
  unsubscriptions: ['hello']
198
217
  })
199
-
200
- s.outStream.once('data', function (packet) {
201
- t.pass('subscribe completed')
202
- })
203
- })
218
+ }
219
+ // run parallel
220
+ await Promise.all([
221
+ checkUnsubscribe(),
222
+ doUnsubscribe()
223
+ ])
204
224
  })
205
225
 
206
- test('emit unsubscribe event if unrecognized params in unsubscribe packet structure', function (t) {
226
+ // TODO unsubscribe event is not emitted
227
+ // remove { skip: true} once this is fixed
228
+ test('emit unsubscribe event if unrecognized params in unsubscribe packet structure', { skip: true }, async (t) => {
207
229
  t.plan(3)
208
230
 
209
- const broker = aedes()
210
- t.teardown(broker.close.bind(broker))
231
+ const s = await createAndConnect(t)
211
232
 
212
- const s = noError(connect(setup(broker)))
213
233
  const unsubs = [{ topic: 'hello', qos: 0 }]
214
234
 
215
- broker.on('unsubscribe', function (unsubscriptions, client) {
216
- t.equal(unsubscriptions, unsubs)
217
- t.same(client, s.client)
218
- })
235
+ const checkUnsubscribe = async () => {
236
+ const [unsubscriptions, client] = await once(s.broker, 'unsubscribe')
237
+ t.assert.equal(unsubscriptions, unsubs)
238
+ t.assert.deepEqual(client, s.client)
239
+ }
219
240
 
220
- s.client.unsubscribe({
221
- unsubscriptions: unsubs,
222
- close: true
223
- }, function (err) {
224
- t.error(err)
241
+ const doUnsubscribe = new Promise((resolve) => {
242
+ s.client.unsubscribe({
243
+ unsubscriptions: unsubs,
244
+ close: true
245
+ }, err => {
246
+ t.assert.ok(!err)
247
+ resolve()
248
+ })
225
249
  })
226
- })
227
250
 
228
- test('dont emit unsubscribe event on client close', function (t) {
229
- t.plan(3)
251
+ // run parallel
252
+ await Promise.all([
253
+ checkUnsubscribe(),
254
+ doUnsubscribe
255
+ ])
256
+ })
230
257
 
231
- const broker = aedes()
232
- t.teardown(broker.close.bind(broker))
258
+ // TODO: Aedes does emit an unsubscribe event on client close
259
+ // remove { skip: true} once this is fixed
260
+ test('dont emit unsubscribe event on client close', { skip: true }, async (t) => {
261
+ t.plan(5)
233
262
 
234
- const s = noError(connect(setup(broker), { clientId: 'abcde' }), t)
263
+ const s = await createAndConnect(t, { connect: { clientId: 'abcde' } })
235
264
 
236
- broker.on('unsubscribe', function (unsubscriptions, client) {
237
- t.error('unsubscribe should not be emitted')
238
- })
265
+ const checkUnsubscribe = async () => {
266
+ const [result] = await withTimeout(once(s.broker, 'unsubscribe'), 100, ['timeout'])
267
+ t.assert.deepEqual(result, 'timeout', 'unsubscribe should not be emitted')
268
+ }
239
269
 
240
- subscribe(t, s, 'hello', 0, function () {
270
+ const doSubscribe = async () => {
271
+ await subscribe(t, s, 'hello', 0)
241
272
  s.inStream.end({
242
273
  cmd: 'disconnect'
243
274
  })
244
- s.outStream.once('data', function (packet) {
245
- t.pass('unsubscribe completed')
246
- })
247
- })
275
+ const packet = await nextPacket(s)
276
+ t.assert.equal(packet, null, 'no packet')
277
+ }
278
+
279
+ // run parallel
280
+ await Promise.all([
281
+ checkUnsubscribe(),
282
+ doSubscribe()
283
+ ])
248
284
  })
249
285
 
250
- test('emit clientDisconnect event', function (t) {
286
+ test('emit clientDisconnect event', async (t) => {
251
287
  t.plan(1)
252
288
 
253
- const broker = aedes()
254
- t.teardown(broker.close.bind(broker))
289
+ const s = await createAndConnect(t, { connect: { clientId: 'abcde' } })
255
290
 
256
- broker.on('clientDisconnect', function (client) {
257
- t.equal(client.id, 'abcde', 'client matches')
258
- })
291
+ const checkDisconnect = async () => {
292
+ const [client] = await once(s.broker, 'clientDisconnect')
293
+ t.assert.equal(client.id, 'abcde', 'client matches')
294
+ }
259
295
 
260
- const s = noError(connect(setup(broker), { clientId: 'abcde' }), t)
296
+ const disconnect = () => {
297
+ s.inStream.end({
298
+ cmd: 'disconnect'
299
+ })
300
+ }
261
301
 
262
- s.inStream.end({
263
- cmd: 'disconnect'
264
- })
265
- s.outStream.resume()
302
+ // run parallel
303
+ await Promise.all([
304
+ checkDisconnect(),
305
+ disconnect()
306
+ ])
266
307
  })
267
308
 
268
- test('emits client', function (t) {
309
+ test('emits client', async (t) => {
269
310
  t.plan(1)
270
311
 
271
- const broker = aedes()
272
- t.teardown(broker.close.bind(broker))
312
+ const broker = await Aedes.createBroker()
313
+ t.after(() => broker.close())
273
314
 
274
- broker.on('client', function (client) {
275
- t.equal(client.id, 'abcde', 'clientId matches')
276
- })
315
+ const checkClient = async () => {
316
+ const [client] = await once(broker, 'client')
317
+ t.assert.equal(client.id, 'abcde', 'client matches')
318
+ }
277
319
 
278
- connect(setup(broker), {
279
- clientId: 'abcde'
280
- })
320
+ const doConnect = async () => {
321
+ const s = setup(broker)
322
+ await connect(s, { connect: { clientId: 'abcde' } })
323
+ }
324
+ // run parallel
325
+ await Promise.all([
326
+ checkClient(),
327
+ doConnect()
328
+ ])
281
329
  })
282
330
 
283
- test('get aedes version', function (t) {
331
+ test('get aedes version', async (t) => {
284
332
  t.plan(1)
285
333
 
286
- const broker = aedes()
287
- t.teardown(broker.close.bind(broker))
334
+ const broker = await Aedes.createBroker()
335
+ t.after(() => broker.close())
288
336
 
289
- t.equal(broker.version, require('../package.json').version)
337
+ t.assert.equal(broker.version, version)
290
338
  })
291
339
 
292
- test('connect and connackSent event', { timeout: 50 }, function (t) {
340
+ test('connect and connackSent event', { timeout: 50 }, async (t) => {
293
341
  t.plan(3)
294
342
 
295
- const s = setup()
296
- t.teardown(s.broker.close.bind(s.broker))
343
+ const broker = await Aedes.createBroker()
344
+ const s = setup(broker)
345
+ t.after(() => broker.close())
297
346
 
298
347
  const clientId = 'my-client'
299
348
 
300
- s.broker.on('connackSent', function (packet, client) {
301
- t.equal(packet.returnCode, 0)
302
- t.equal(client.id, clientId, 'connackSent event and clientId matches')
303
- })
304
-
305
- s.inStream.write({
306
- cmd: 'connect',
307
- protocolId: 'MQTT',
308
- protocolVersion: 4,
309
- clean: true,
310
- clientId,
311
- keepalive: 0
312
- })
349
+ const checkConnack = async () => {
350
+ const [packet, client] = await once(s.broker, 'connackSent')
351
+ t.assert.equal(packet.returnCode, 0)
352
+ t.assert.equal(client.id, clientId, 'connackSent event and clientId matches')
353
+ }
313
354
 
314
- s.outStream.on('data', function (packet) {
315
- t.same(packet, {
355
+ const doConnect = async () => {
356
+ const packet = await connect(s, { connect: { clientId, clean: true, keepalive: 0 } })
357
+ t.assert.deepEqual(structuredClone(packet), {
316
358
  cmd: 'connack',
317
359
  returnCode: 0,
318
360
  length: 2,
@@ -323,5 +365,10 @@ test('connect and connackSent event', { timeout: 50 }, function (t) {
323
365
  payload: null,
324
366
  sessionPresent: false
325
367
  }, 'successful connack')
326
- })
368
+ }
369
+ // run parallel
370
+ await Promise.all([
371
+ checkConnack(),
372
+ doConnect()
373
+ ])
327
374
  })