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.
Files changed (68) hide show
  1. package/.claude/settings.local.json +12 -0
  2. package/.github/actions/sticky-pr-comment/action.yml +55 -0
  3. package/.github/workflows/benchmark-compare-serial.yml +60 -0
  4. package/.github/workflows/ci.yml +12 -17
  5. package/.release-it.json +18 -0
  6. package/.taprc +15 -6
  7. package/README.md +6 -4
  8. package/aedes.d.ts +0 -6
  9. package/aedes.js +270 -242
  10. package/benchmarks/README.md +33 -0
  11. package/benchmarks/pingpong.js +94 -25
  12. package/benchmarks/receiver.js +77 -0
  13. package/benchmarks/report.js +150 -0
  14. package/benchmarks/runBenchmarks.js +118 -0
  15. package/benchmarks/sender.js +86 -0
  16. package/benchmarks/server.js +19 -18
  17. package/checkVersion.js +20 -0
  18. package/docs/Aedes.md +66 -8
  19. package/docs/Client.md +3 -4
  20. package/docs/Examples.md +39 -22
  21. package/docs/MIGRATION.md +50 -0
  22. package/eslint.config.js +8 -0
  23. package/example.js +51 -40
  24. package/examples/clusters/index.js +28 -23
  25. package/examples/clusters/package.json +10 -6
  26. package/lib/client.js +405 -306
  27. package/lib/handlers/connect.js +42 -38
  28. package/lib/handlers/index.js +9 -11
  29. package/lib/handlers/ping.js +2 -3
  30. package/lib/handlers/puback.js +5 -5
  31. package/lib/handlers/publish.js +29 -14
  32. package/lib/handlers/pubrec.js +9 -17
  33. package/lib/handlers/pubrel.js +34 -25
  34. package/lib/handlers/subscribe.js +47 -43
  35. package/lib/handlers/unsubscribe.js +16 -19
  36. package/lib/qos-packet.js +14 -17
  37. package/lib/utils.js +5 -12
  38. package/lib/write.js +4 -5
  39. package/package.json +139 -136
  40. package/test/auth.js +468 -804
  41. package/test/basic.js +613 -575
  42. package/test/bridge.js +44 -40
  43. package/test/client-pub-sub.js +531 -504
  44. package/test/close_socket_by_other_party.js +137 -102
  45. package/test/connect.js +487 -484
  46. package/test/drain-timeout.js +593 -0
  47. package/test/drain-toxiproxy.js +620 -0
  48. package/test/events.js +173 -145
  49. package/test/helper.js +351 -73
  50. package/test/keep-alive.js +40 -67
  51. package/test/meta.js +257 -210
  52. package/test/not-blocking.js +93 -197
  53. package/test/qos1.js +464 -554
  54. package/test/qos2.js +308 -393
  55. package/test/regr-21.js +39 -21
  56. package/test/require.cjs +22 -0
  57. package/test/retain.js +349 -398
  58. package/test/topics.js +176 -183
  59. package/test/types/aedes.test-d.ts +4 -8
  60. package/test/will.js +310 -428
  61. package/types/instance.d.ts +40 -35
  62. package/types/packet.d.ts +10 -10
  63. package/.coveralls.yml +0 -1
  64. package/benchmarks/bombing.js +0 -34
  65. package/benchmarks/bombingQoS1.js +0 -36
  66. package/benchmarks/throughputCounter.js +0 -23
  67. package/benchmarks/throughputCounterQoS1.js +0 -33
  68. package/types/.eslintrc.json +0 -47
package/test/events.js CHANGED
@@ -1,27 +1,46 @@
1
- 'use strict'
2
-
3
- const { test } = require('tap')
4
- const mqemitter = require('mqemitter')
5
- const { setup, connect, subscribe } = require('./helper')
6
- const aedes = require('../')
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', function (t) {
24
+ test('publishes an hearbeat', async (t) => {
9
25
  t.plan(2)
10
26
 
11
- const broker = aedes({
27
+ const broker = await Aedes.createBroker({
12
28
  heartbeatInterval: 10 // ms
13
29
  })
14
- t.teardown(broker.close.bind(broker))
30
+ t.after(() => broker.close())
15
31
 
16
- broker.subscribe('$SYS/+/heartbeat', function (message, cb) {
17
- const id = message.topic.match(/\$SYS\/([^/]+)\/heartbeat/)[1]
18
- t.equal(id, broker.id, 'broker id matches')
19
- t.same(message.payload.toString(), id, 'message has id as the payload')
20
- cb()
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', function (t) {
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.pass('broker birth received')
34
- t.same(message.payload.toString(), brokerId, 'message has id as the payload')
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 broker = aedes({
39
- id: brokerId,
40
- mq
57
+ const s = await createAndConnect(t, {
58
+ broker: {
59
+ id: brokerId,
60
+ mq
61
+ },
62
+ connect: {
63
+ clientId
64
+ }
41
65
  })
42
66
 
43
- broker.on('client', (client) => {
44
- t.equal(client.id, clientId, 'client connected')
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
- mq.on(`$SYS/${fakeBroker}/birth`, (message, cb) => {
53
- process.nextTick(() => {
54
- t.equal(!!broker.clients[clientId].duplicates[fakeBroker], false, 'client duplicates has been resetted')
55
- cb()
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
- ;['$mcollina', '$SYS'].forEach(function (topic) {
64
- test('does not forward $ prefixed topics to # subscription - ' + topic, function (t) {
65
- t.plan(4)
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 = connect(setup())
68
- t.teardown(s.broker.close.bind(s.broker))
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
- }, function () {
80
- t.pass('nothing happened')
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, function (t) {
86
- t.plan(4)
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
- subscribe(t, s, '+/#', 0, function () {
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
- }, function () {
101
- t.pass('nothing happened')
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
- const broker = aedes()
111
- t.teardown(broker.close.bind(broker))
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
- let s = connect(setup(broker), opts)
131
+ const opts = { connect: { clean: false, clientId: 'abcde' } }
132
+ const s1 = await createAndConnect(t, opts)
115
133
 
116
- subscribe(t, s, '#', 1, function () {
117
- s.inStream.end()
134
+ await subscribe(t, s1, '#', 1)
135
+ s1.inStream.end()
118
136
 
119
- s.broker.publish({
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
- s.outStream.once('data', function (packet) {
128
- t.fail('no packet should be received')
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 broker = aedes()
138
- t.teardown(broker.close.bind(broker))
146
+ const s2 = setup(s1.broker)
147
+ await connect(s2, opts)
139
148
 
140
- broker.on('ping', function (packet, client) {
141
- if (client && client) {
142
- t.equal(client.id, 'abcde')
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
- const s = connect(setup(broker), { clientId: 'abcde' })
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', function (t) {
172
+ test('Emit event when broker closed', async (t) => {
158
173
  t.plan(1)
159
174
 
160
- const broker = aedes()
161
- broker.once('closed', function () {
162
- t.ok(true)
163
- })
164
- broker.close()
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 one only when double broker.close()', function (t) {
184
+ test('Emit closed event only once when double broker.close()', async (t) => {
168
185
  t.plan(4)
169
186
 
170
- const broker = aedes()
171
- broker.on('closed', function () {
172
- t.pass('closed')
173
- })
174
- t.notOk(broker.closed)
175
- broker.close()
176
- t.ok(broker.closed)
177
- broker.close()
178
- t.ok(broker.closed)
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', function (t) {
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 = aedes({
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
- const mqtt = require('mqtt')
200
- const server = require('net').createServer(broker.handle)
201
- let publisher
202
-
203
- server.listen(0, function () {
204
- const port = server.address().port
205
- publisher = mqtt.connect({ port, host: 'localhost', clean: true, keepalive: 30 })
206
-
207
- function next () {
208
- if (--publishCount > 0) { process.nextTick(publish) }
209
- }
210
-
211
- function publish () {
212
- publisher.publish('test', 'payload', next)
213
- }
214
-
215
- publisher.on('connect', publish)
216
- publisher.on('end', function () {
217
- t.ok(count > publishCount)
218
- t.equal(publishCount, 0)
219
- broker.close()
220
- server.close()
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', function (t) {
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 aedesBroker = aedes()
230
-
256
+ const broker = await Aedes.createBroker()
257
+ t.after(() => broker.close())
231
258
  // simulate a closed client on the broker
232
- aedesBroker.clients[clientId] = { closed: true, broker: aedesBroker }
233
- aedesBroker.connectedClients = 1
234
-
235
- // simulate the creation of the same client on another broker of the cluster
236
- aedesBroker.publish({ topic: '$SYS/anotherbroker/new/clients', payload: clientId }, () => {
237
- t.equal(aedesBroker.clients[clientId], undefined) // check that the closed client was removed
238
- t.equal(aedesBroker.connectedClients, 0)
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
  })