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
@@ -1,176 +1,211 @@
1
- 'use strict'
2
-
3
- const { test } = require('tap')
4
- const EventEmitter = require('events')
5
- const { setup, connect, subscribe } = require('./helper')
6
- const aedes = require('../')
7
-
8
- test('aedes is closed before client authenticate returns', function (t) {
1
+ import { test } from 'node:test'
2
+ import EventEmitter, { once } from 'node:events'
3
+ import { createServer } from 'node:net'
4
+ import {
5
+ connect,
6
+ createAndConnect,
7
+ delay,
8
+ setup,
9
+ subscribe,
10
+ withTimeout
11
+ } from './helper.js'
12
+ import { Aedes } from '../aedes.js'
13
+ import mqtt from 'mqtt'
14
+
15
+ test('aedes is closed before client authenticate returns', async (t) => {
9
16
  t.plan(1)
10
17
 
11
18
  const evt = new EventEmitter()
12
- const broker = aedes({
19
+ const broker = await Aedes.createBroker({
13
20
  authenticate: (client, username, password, done) => {
14
21
  evt.emit('AuthenticateBegin', client)
15
- setTimeout(function () {
22
+ setTimeout(() => {
16
23
  done(null, true)
17
- }, 2000)
24
+ }, 20)
18
25
  }
19
26
  })
20
-
21
- broker.on('client', function (client) {
22
- t.fail('should no client registration')
27
+ broker.on('client', () => {
28
+ t.assert.fail('should no client registration')
23
29
  })
24
- broker.on('connackSent', function () {
25
- t.fail('should no connack be sent')
30
+ broker.on('connackSent', () => {
31
+ t.assert.fail('should no connack be sent')
26
32
  })
27
- broker.on('clientError', function (client, err) {
28
- t.error(err)
33
+ broker.on('clientError', () => {
34
+ t.assert.fail('should not error')
29
35
  })
30
36
 
31
- connect(setup(broker))
37
+ const s = setup(broker)
32
38
 
33
- evt.on('AuthenticateBegin', function (client) {
34
- t.equal(broker.connectedClients, 0)
39
+ const waitForAuthEvent = async () => {
40
+ await once(evt, 'AuthenticateBegin')
41
+ t.assert.equal(broker.connectedClients, 0)
35
42
  broker.close()
36
- })
43
+ }
44
+
45
+ // run parallel
46
+ await Promise.all([
47
+ waitForAuthEvent(),
48
+ withTimeout(connect(s), 0, 'connect timed out'), // connect will never finish
49
+ ])
37
50
  })
38
51
 
39
- test('client is closed before authenticate returns', function (t) {
52
+ test('client is closed before authenticate returns', async (t) => {
40
53
  t.plan(1)
41
54
 
42
55
  const evt = new EventEmitter()
43
- const broker = aedes({
44
- authenticate: async (client, username, password, done) => {
56
+ const broker = await Aedes.createBroker({
57
+ authenticate: (client, username, password, done) => {
45
58
  evt.emit('AuthenticateBegin', client)
46
- setTimeout(function () {
59
+ setTimeout(() => {
47
60
  done(null, true)
48
- }, 2000)
61
+ }, 20)
49
62
  }
50
63
  })
51
- t.teardown(broker.close.bind(broker))
52
64
 
53
- broker.on('client', function (client) {
54
- t.fail('should no client registration')
65
+ t.after(() => broker.close())
66
+
67
+ broker.on('client', () => {
68
+ t.assert.fail('should no client registration')
55
69
  })
56
- broker.on('connackSent', function () {
57
- t.fail('should no connack be sent')
70
+ broker.on('connackSent', () => {
71
+ t.assert.fail('should no connack be sent')
58
72
  })
59
- broker.on('clientError', function (client, err) {
60
- t.error(err)
73
+ broker.on('clientError', () => {
74
+ t.assert.fail('should not error')
61
75
  })
62
76
 
63
- connect(setup(broker))
77
+ const s = setup(broker)
64
78
 
65
- evt.on('AuthenticateBegin', function (client) {
66
- t.equal(broker.connectedClients, 0)
79
+ const waitForAuthEvent = async () => {
80
+ const [client] = await once(evt, 'AuthenticateBegin')
81
+ t.assert.equal(broker.connectedClients, 0)
67
82
  client.close()
68
- })
83
+ }
84
+
85
+ // run parallel
86
+ await Promise.all([
87
+ waitForAuthEvent(),
88
+ // connect will never finish, but the client close will produce a null in the generator
89
+ withTimeout(connect(s, { verifyIsConnack: false }), 10, 'connect timed out'),
90
+ ])
69
91
  })
70
92
 
71
- test('client is closed before authorizePublish returns', function (t) {
72
- t.plan(3)
93
+ test('client is closed before authorizePublish returns', async (t) => {
94
+ t.plan(4)
73
95
 
74
96
  const evt = new EventEmitter()
75
- const broker = aedes({
97
+ const broker = await Aedes.createBroker({
76
98
  authorizePublish: (client, packet, done) => {
77
99
  evt.emit('AuthorizePublishBegin', client)
78
100
  // simulate latency writing to persistent store.
79
- setTimeout(function () {
101
+ setTimeout(() => {
80
102
  done()
81
103
  evt.emit('AuthorizePublishEnd', client)
82
- }, 2000)
104
+ }, 50)
83
105
  }
84
106
  })
107
+ t.after(() => broker.close())
85
108
 
86
- broker.on('clientError', function (client, err) {
87
- t.equal(err.message, 'connection closed')
88
- })
109
+ const s = setup(broker)
110
+ await connect(s)
89
111
 
90
- const s = connect(setup(broker))
91
- s.inStream.write({
92
- cmd: 'publish',
93
- topic: 'hello',
94
- payload: 'world',
95
- qos: 1,
96
- messageId: 10,
97
- retain: false
98
- })
112
+ const connectionClosed = async () => {
113
+ const [client, err] = await once(broker, 'clientError')
114
+ t.assert.ok(client, 'client exists')
115
+ t.assert.equal(err.message, 'connection closed', 'connection is closed')
116
+ }
99
117
 
100
- evt.on('AuthorizePublishBegin', function (client) {
101
- t.equal(broker.connectedClients, 1)
118
+ const publishBegin = async () => {
119
+ const [client] = await once(evt, 'AuthorizePublishBegin')
120
+ t.assert.equal(broker.connectedClients, 1, '1 client connected')
121
+ await delay(0) // give the eventloop some time
102
122
  client.close()
103
- })
104
- evt.on('AuthorizePublishEnd', function (client) {
105
- t.equal(broker.connectedClients, 0)
106
- broker.close()
107
- })
123
+ }
124
+
125
+ const publishEnd = async () => {
126
+ await once(evt, 'AuthorizePublishEnd')
127
+ t.assert.equal(broker.connectedClients, 0, 'no client connected')
128
+ }
129
+
130
+ const publish = () => {
131
+ s.inStream.write({
132
+ cmd: 'publish',
133
+ topic: 'hello',
134
+ payload: 'world',
135
+ qos: 1,
136
+ messageId: 10,
137
+ retain: false
138
+ })
139
+ }
140
+ // run parallel
141
+ await Promise.all([
142
+ connectionClosed(),
143
+ publishBegin(),
144
+ publishEnd(),
145
+ publish()
146
+ ])
108
147
  })
109
148
 
110
- test('close client when its socket is closed', function (t) {
149
+ test('close client when its socket is closed', async (t) => {
111
150
  t.plan(4)
112
151
 
113
- const broker = aedes()
114
- t.teardown(broker.close.bind(broker))
115
-
116
- const subscriber = connect(setup(broker))
117
-
118
- subscribe(t, subscriber, 'hello', 1, function () {
119
- subscriber.inStream.end()
120
- subscriber.conn.on('close', function () {
121
- t.equal(broker.connectedClients, 0, 'no connected client')
122
- })
123
- })
152
+ const s = await createAndConnect(t)
153
+ await subscribe(t, s, 'hello', 1)
154
+ s.inStream.end()
155
+ await once(s.client.conn, 'close')
156
+ await delay(10)
157
+ t.assert.equal(s.broker.connectedClients, 0, 'no connected client')
124
158
  })
125
159
 
126
- test('multiple clients subscribe same topic, and all clients still receive message except the closed one', function (t) {
160
+ test('multiple clients subscribe same topic, and all clients still receive message except the closed one', async (t) => {
127
161
  t.plan(5)
128
162
 
129
- const mqtt = require('mqtt')
130
- const broker = aedes()
131
-
163
+ const broker = await Aedes.createBroker()
132
164
  let client2
133
165
 
134
- t.teardown(() => {
166
+ t.after(() => {
135
167
  client2.end()
136
168
  broker.close()
137
169
  server.close()
138
170
  })
139
171
 
140
- const server = require('net').createServer(broker.handle)
172
+ const server = createServer(broker.handle)
141
173
  const port = 1883
142
174
  server.listen(port)
143
- broker.on('clientError', function (client, err) {
144
- t.error(err)
175
+ broker.on('clientError', () => {
176
+ t.assert.fail('should not get clientError event')
145
177
  })
146
178
 
147
- const _sameTopic = 'hello'
179
+ const sameTopic = 'hello'
148
180
 
149
181
  // client 1
150
182
  const client1 = mqtt.connect('mqtt://localhost', { clientId: 'client1', resubscribe: false, reconnectPeriod: -1 })
151
183
  client1.on('message', () => {
152
- t.fail('client1 receives message')
184
+ t.assert.fail('client1 receives message')
153
185
  })
154
186
 
155
- client1.subscribe(_sameTopic, { qos: 0, retain: false }, () => {
156
- t.pass('client1 sub callback')
157
- // stimulate closed socket by users
158
- client1.stream.destroy()
187
+ await new Promise(resolve => {
188
+ client1.subscribe(sameTopic, { qos: 0, retain: false }, () => {
189
+ t.assert.ok(true, 'client1 sub callback')
190
+ // stimulate closed socket by users
191
+ client1.stream.destroy()
159
192
 
160
- // client 2
161
- client2 = mqtt.connect('mqtt://localhost', { clientId: 'client2', resubscribe: false })
162
- client2.on('message', () => {
163
- t.pass('client2 receives message')
164
- t.equal(broker.connectedClients, 1)
165
- })
166
- client2.subscribe(_sameTopic, { qos: 0, retain: false }, () => {
167
- t.pass('client2 sub callback')
168
-
169
- // pubClient
170
- const pubClient = mqtt.connect('mqtt://localhost', { clientId: 'pubClient' })
171
- pubClient.publish(_sameTopic, 'world', { qos: 0, retain: false }, () => {
172
- t.pass('pubClient publish event')
173
- pubClient.end()
193
+ // client 2
194
+ client2 = mqtt.connect('mqtt://localhost', { clientId: 'client2', resubscribe: false })
195
+ client2.on('message', () => {
196
+ t.assert.ok(true, 'client2 receives message')
197
+ t.assert.equal(broker.connectedClients, 1)
198
+ resolve()
199
+ })
200
+ client2.subscribe(sameTopic, { qos: 0, retain: false }, () => {
201
+ t.assert.ok(true, 'client2 sub callback')
202
+
203
+ // pubClient
204
+ const pubClient = mqtt.connect('mqtt://localhost', { clientId: 'pubClient' })
205
+ pubClient.publish(sameTopic, 'world', { qos: 0, retain: false }, () => {
206
+ t.assert.ok(true, 'pubClient publish event')
207
+ pubClient.end()
208
+ })
174
209
  })
175
210
  })
176
211
  })