aedes 0.45.2 → 0.46.3
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/.github/workflows/ci.yml +3 -3
- package/.taprc +2 -1
- package/README.md +1 -3
- package/aedes.js +10 -6
- package/docs/Aedes.md +13 -2
- package/lib/handlers/subscribe.js +34 -24
- package/lib/handlers/unsubscribe.js +5 -5
- package/lib/utils.js +2 -1
- package/package.json +25 -24
- package/test/auth.js +13 -1
- package/test/basic.js +46 -0
- package/test/qos1.js +22 -38
- package/test/retain.js +37 -0
- package/types/client.d.ts +2 -1
- package/types/instance.d.ts +10 -2
package/.github/workflows/ci.yml
CHANGED
|
@@ -16,14 +16,14 @@ jobs:
|
|
|
16
16
|
|
|
17
17
|
strategy:
|
|
18
18
|
matrix:
|
|
19
|
-
node-version: [
|
|
19
|
+
node-version: [12.x, 14.x, 16.x]
|
|
20
20
|
os: [ubuntu-latest, windows-latest, macOS-latest]
|
|
21
21
|
|
|
22
22
|
steps:
|
|
23
|
-
- uses: actions/checkout@v2
|
|
23
|
+
- uses: actions/checkout@v2.4.0
|
|
24
24
|
|
|
25
25
|
- name: Use Node.js
|
|
26
|
-
uses: actions/setup-node@v2.1
|
|
26
|
+
uses: actions/setup-node@v2.5.1
|
|
27
27
|
with:
|
|
28
28
|
node-version: ${{ matrix.node-version }}
|
|
29
29
|
|
package/.taprc
CHANGED
package/README.md
CHANGED
|
@@ -7,10 +7,8 @@
|
|
|
7
7
|
[](https://github.com/moscajs/aedes/pulls)\
|
|
8
8
|
[](https://lgtm.com/projects/g/moscajs/aedes/alerts/)
|
|
9
9
|
[](https://lgtm.com/projects/g/moscajs/aedes/context:javascript)
|
|
10
|
-
[](https://coveralls.io/github/moscajs/aedes?branch=main)
|
|
11
11
|
[](https://snyk.io/test/github/moscajs/aedes)\
|
|
12
|
-
[](https://david-dm.org/moscajs/aedes)
|
|
13
|
-
[](https://david-dm.org/moscajs/aedes?type=dev)\
|
|
14
12
|

|
|
15
13
|
[](https://www.npmjs.com/aedes)
|
|
16
14
|
[](https://www.npmjs.com/aedes)
|
package/aedes.js
CHANGED
|
@@ -12,6 +12,7 @@ const Packet = require('aedes-packet')
|
|
|
12
12
|
const memory = require('aedes-persistence')
|
|
13
13
|
const mqemitter = require('mqemitter')
|
|
14
14
|
const Client = require('./lib/client')
|
|
15
|
+
const { $SYS_PREFIX } = require('./lib/utils')
|
|
15
16
|
|
|
16
17
|
module.exports = Aedes.Server = Aedes
|
|
17
18
|
|
|
@@ -78,7 +79,7 @@ function Aedes (opts) {
|
|
|
78
79
|
this.clients = {}
|
|
79
80
|
this.brokers = {}
|
|
80
81
|
|
|
81
|
-
const heartbeatTopic =
|
|
82
|
+
const heartbeatTopic = $SYS_PREFIX + that.id + '/heartbeat'
|
|
82
83
|
this._heartbeatInterval = setInterval(heartbeat, opts.heartbeatInterval)
|
|
83
84
|
|
|
84
85
|
const bufId = Buffer.from(that.id, 'utf8')
|
|
@@ -138,12 +139,12 @@ function Aedes (opts) {
|
|
|
138
139
|
}
|
|
139
140
|
}
|
|
140
141
|
|
|
141
|
-
this.mq.on('
|
|
142
|
+
this.mq.on($SYS_PREFIX + '+/heartbeat', function storeBroker (packet, done) {
|
|
142
143
|
that.brokers[packet.payload.toString()] = Date.now()
|
|
143
144
|
done()
|
|
144
145
|
})
|
|
145
146
|
|
|
146
|
-
this.mq.on('
|
|
147
|
+
this.mq.on($SYS_PREFIX + '+/new/clients', function closeSameClients (packet, done) {
|
|
147
148
|
const serverId = packet.topic.split('/')[1]
|
|
148
149
|
const clientId = packet.payload.toString()
|
|
149
150
|
|
|
@@ -206,7 +207,7 @@ function DoEnqueues () {
|
|
|
206
207
|
return
|
|
207
208
|
}
|
|
208
209
|
|
|
209
|
-
if (that.topic.indexOf(
|
|
210
|
+
if (that.topic.indexOf($SYS_PREFIX) === 0) {
|
|
210
211
|
subs = subs.filter(removeSharp)
|
|
211
212
|
}
|
|
212
213
|
|
|
@@ -281,7 +282,7 @@ Aedes.prototype._finishRegisterClient = function (client) {
|
|
|
281
282
|
this.clients[client.id] = client
|
|
282
283
|
this.emit('client', client)
|
|
283
284
|
this.publish({
|
|
284
|
-
topic:
|
|
285
|
+
topic: $SYS_PREFIX + this.id + '/new/clients',
|
|
285
286
|
payload: Buffer.from(client.id, 'utf8')
|
|
286
287
|
}, noop)
|
|
287
288
|
}
|
|
@@ -291,7 +292,7 @@ Aedes.prototype.unregisterClient = function (client) {
|
|
|
291
292
|
delete this.clients[client.id]
|
|
292
293
|
this.emit('clientDisconnect', client)
|
|
293
294
|
this.publish({
|
|
294
|
-
topic:
|
|
295
|
+
topic: $SYS_PREFIX + this.id + '/disconnect/clients',
|
|
295
296
|
payload: Buffer.from(client.id, 'utf8')
|
|
296
297
|
}, noop)
|
|
297
298
|
}
|
|
@@ -326,6 +327,9 @@ function defaultAuthenticate (client, username, password, callback) {
|
|
|
326
327
|
}
|
|
327
328
|
|
|
328
329
|
function defaultAuthorizePublish (client, packet, callback) {
|
|
330
|
+
if (packet.topic.startsWith($SYS_PREFIX)) {
|
|
331
|
+
return callback(new Error($SYS_PREFIX + ' topic is reserved'))
|
|
332
|
+
}
|
|
329
333
|
callback(null)
|
|
330
334
|
}
|
|
331
335
|
|
package/docs/Aedes.md
CHANGED
|
@@ -113,7 +113,7 @@ Emitted when timeout happes in the `client` keepalive.
|
|
|
113
113
|
- `packet` `<aedes-packet>` & [`PUBLISH`][PUBLISH]
|
|
114
114
|
- `client` [`<Client>`](./Client.md) | `null`
|
|
115
115
|
|
|
116
|
-
Emitted when servers delivers the `packet` to subscribed `client`. If there are no clients subscribed to the `packet` topic, server still publish the `packet` and emit
|
|
116
|
+
Emitted when servers delivers the `packet` to subscribed `client`. If there are no clients subscribed to the `packet` topic, server still publish the `packet` and emit the event. `client` is `null` when `packet` is an internal message like aedes heartbeat message and LWT.
|
|
117
117
|
|
|
118
118
|
> _Note! `packet` belongs `aedes-packet` type. Some properties belongs to aedes internal, any changes on them will break aedes internal flow._
|
|
119
119
|
|
|
@@ -315,6 +315,17 @@ aedes.authorizePublish = function (client, packet, callback) {
|
|
|
315
315
|
}
|
|
316
316
|
```
|
|
317
317
|
|
|
318
|
+
By default `authorizePublish` throws error in case a client publish to topics with `$SYS/` prefix to prevent possible DoS (see [#597](https://github.com/moscajs/aedes/issues/597)). If you write your own implementation of `authorizePublish` we suggest you to add a check for this. Default implementation:
|
|
319
|
+
|
|
320
|
+
```js
|
|
321
|
+
function defaultAuthorizePublish (client, packet, callback) {
|
|
322
|
+
if (packet.topic.startsWith($SYS_PREFIX)) {
|
|
323
|
+
return callback(new Error($SYS_PREFIX + ' topic is reserved'))
|
|
324
|
+
}
|
|
325
|
+
callback(null)
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
318
329
|
## Handler: authorizeSubscribe (client, subscription, callback)
|
|
319
330
|
|
|
320
331
|
- client: [`<Client>`](./Client.md)
|
|
@@ -392,7 +403,7 @@ aedes.authorizeForward = function (client, packet) {
|
|
|
392
403
|
- client: [`<Client>`](./Client.md)
|
|
393
404
|
- callback: `<Function>`
|
|
394
405
|
|
|
395
|
-
same as [`Event: publish`](#event-publish), but provides a backpressure functionality.
|
|
406
|
+
same as [`Event: publish`](#event-publish), but provides a backpressure functionality. TLDR; If you are doing operations on packets that MUST require finishing operations on a packet before handling the next one use this otherwise, expecially for long running operations, you should use [`Event: publish`](#event-publish) instead.
|
|
396
407
|
|
|
397
408
|
[CONNECT]: https://github.com/mqttjs/mqtt-packet#connect
|
|
398
409
|
[CONNACK]: https://github.com/mqttjs/mqtt-packet#connack
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const fastfall = require('fastfall')
|
|
4
4
|
const Packet = require('aedes-packet')
|
|
5
5
|
const { through } = require('../utils')
|
|
6
|
-
const { validateTopic } = require('../utils')
|
|
6
|
+
const { validateTopic, $SYS_PREFIX } = require('../utils')
|
|
7
7
|
const write = require('../write')
|
|
8
8
|
|
|
9
9
|
const subscribeTopicActions = fastfall([
|
|
@@ -76,10 +76,11 @@ function _dedupe (subs) {
|
|
|
76
76
|
function handleSubscribe (client, packet, restore, done) {
|
|
77
77
|
packet.subscriptions = packet.subscriptions.length === 1 ? packet.subscriptions : _dedupe(packet.subscriptions)
|
|
78
78
|
client.broker._parallel(
|
|
79
|
-
new SubscribeState(client, packet, restore, done),
|
|
80
|
-
doSubscribe,
|
|
81
|
-
packet.subscriptions,
|
|
82
|
-
restore ? done : completeSubscribe
|
|
79
|
+
new SubscribeState(client, packet, restore, done), // what will be this in the functions
|
|
80
|
+
doSubscribe, // function to call
|
|
81
|
+
packet.subscriptions, // first argument of the function
|
|
82
|
+
restore ? done : completeSubscribe // the function to be called when the parallel ends
|
|
83
|
+
)
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
function doSubscribe (sub, done) {
|
|
@@ -184,7 +185,7 @@ function completeSubscribe (err) {
|
|
|
184
185
|
const packet = this.packet
|
|
185
186
|
const client = this.client
|
|
186
187
|
|
|
187
|
-
if (packet.messageId) {
|
|
188
|
+
if (packet.messageId !== undefined) {
|
|
188
189
|
// [MQTT-3.9.3-1]
|
|
189
190
|
write(client,
|
|
190
191
|
new SubAck(packet, this.subState.map(obj => obj.granted)),
|
|
@@ -194,20 +195,27 @@ function completeSubscribe (err) {
|
|
|
194
195
|
}
|
|
195
196
|
|
|
196
197
|
const broker = client.broker
|
|
198
|
+
|
|
199
|
+
// subscriptions array to return as result in 'subscribe' event and $SYS
|
|
197
200
|
const subs = packet.subscriptions
|
|
198
201
|
|
|
202
|
+
// topics we need to retrieve retain values from
|
|
199
203
|
const topics = []
|
|
200
204
|
|
|
201
205
|
for (let i = 0; i < subs.length; i++) {
|
|
202
|
-
topics
|
|
203
|
-
|
|
206
|
+
// skip topics that are not allowed
|
|
207
|
+
if (this.subState[i].granted !== 128) {
|
|
208
|
+
topics.push(subs[i].topic)
|
|
209
|
+
}
|
|
210
|
+
// set granted qos to subscriptions
|
|
211
|
+
subs[i].qos = this.subState[i].granted
|
|
204
212
|
}
|
|
205
213
|
|
|
206
214
|
this.subState = []
|
|
207
215
|
|
|
208
216
|
broker.emit('subscribe', subs, client)
|
|
209
217
|
broker.publish({
|
|
210
|
-
topic:
|
|
218
|
+
topic: $SYS_PREFIX + broker.id + '/new/subscribes',
|
|
211
219
|
payload: Buffer.from(JSON.stringify({
|
|
212
220
|
clientId: client.id,
|
|
213
221
|
subs: subs
|
|
@@ -218,22 +226,24 @@ function completeSubscribe (err) {
|
|
|
218
226
|
// Restored sessions should not contain any retained message.
|
|
219
227
|
// Retained message should be only fetched from SUBSCRIBE.
|
|
220
228
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
packet
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
229
|
+
if (topics.length > 0) {
|
|
230
|
+
const persistence = broker.persistence
|
|
231
|
+
const stream = persistence.createRetainedStreamCombi(topics)
|
|
232
|
+
stream.pipe(through(function sendRetained (packet, enc, cb) {
|
|
233
|
+
packet = new Packet({
|
|
234
|
+
cmd: packet.cmd,
|
|
235
|
+
qos: packet.qos,
|
|
236
|
+
topic: packet.topic,
|
|
237
|
+
payload: packet.payload,
|
|
238
|
+
retain: true
|
|
239
|
+
}, broker)
|
|
240
|
+
// this should not be deduped
|
|
241
|
+
packet.brokerId = null
|
|
242
|
+
client.deliverQoS(packet, cb)
|
|
243
|
+
}))
|
|
244
|
+
}
|
|
235
245
|
}
|
|
236
246
|
|
|
237
|
-
function noop () {}
|
|
247
|
+
function noop () { }
|
|
238
248
|
|
|
239
249
|
module.exports = handleSubscribe
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const write = require('../write')
|
|
4
|
-
const { validateTopic } = require('../utils')
|
|
4
|
+
const { validateTopic, $SYS_PREFIX } = require('../utils')
|
|
5
5
|
|
|
6
6
|
function UnSubAck (packet) {
|
|
7
7
|
this.cmd = 'unsuback'
|
|
@@ -26,7 +26,7 @@ function handleUnsubscribe (client, packet, done) {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
if (packet.messageId) {
|
|
29
|
+
if (packet.messageId !== undefined) {
|
|
30
30
|
if (client.clean) {
|
|
31
31
|
return actualUnsubscribe(client, packet, done)
|
|
32
32
|
}
|
|
@@ -80,7 +80,7 @@ function completeUnsubscribe (err) {
|
|
|
80
80
|
const packet = this.packet
|
|
81
81
|
const done = this.finish
|
|
82
82
|
|
|
83
|
-
if (packet.messageId) {
|
|
83
|
+
if (packet.messageId !== undefined) {
|
|
84
84
|
write(client, new UnSubAck(packet),
|
|
85
85
|
done)
|
|
86
86
|
} else {
|
|
@@ -90,7 +90,7 @@ function completeUnsubscribe (err) {
|
|
|
90
90
|
if ((!client.closed || client.clean === true) && packet.unsubscriptions.length > 0) {
|
|
91
91
|
client.broker.emit('unsubscribe', packet.unsubscriptions, client)
|
|
92
92
|
client.broker.publish({
|
|
93
|
-
topic:
|
|
93
|
+
topic: $SYS_PREFIX + client.broker.id + '/new/unsubscribes',
|
|
94
94
|
payload: Buffer.from(JSON.stringify({
|
|
95
95
|
clientId: client.id,
|
|
96
96
|
subs: packet.unsubscriptions
|
|
@@ -99,6 +99,6 @@ function completeUnsubscribe (err) {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
function noop () {}
|
|
102
|
+
function noop () { }
|
|
103
103
|
|
|
104
104
|
module.exports = handleUnsubscribe
|
package/lib/utils.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aedes",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.3",
|
|
4
4
|
"description": "Stream-based MQTT broker",
|
|
5
5
|
"main": "aedes.js",
|
|
6
6
|
"types": "aedes.d.ts",
|
|
@@ -49,13 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"repository": {
|
|
51
51
|
"type": "git",
|
|
52
|
-
"url": "https://github.com/moscajs/aedes.git"
|
|
53
|
-
},
|
|
54
|
-
"bugs": {
|
|
55
|
-
"url": "http://github.com/moscajs/aedes/issues"
|
|
56
|
-
},
|
|
57
|
-
"engines": {
|
|
58
|
-
"node": ">=10"
|
|
52
|
+
"url": "git+https://github.com/moscajs/aedes.git"
|
|
59
53
|
},
|
|
60
54
|
"keywords": [
|
|
61
55
|
"mqtt",
|
|
@@ -95,25 +89,32 @@
|
|
|
95
89
|
}
|
|
96
90
|
],
|
|
97
91
|
"license": "MIT",
|
|
92
|
+
"bugs": {
|
|
93
|
+
"url": "https://github.com/moscajs/aedes/issues"
|
|
94
|
+
},
|
|
95
|
+
"homepage": "https://github.com/moscajs/aedes#readme",
|
|
96
|
+
"engines": {
|
|
97
|
+
"node": ">=12"
|
|
98
|
+
},
|
|
98
99
|
"devDependencies": {
|
|
99
|
-
"@sinonjs/fake-timers": "^
|
|
100
|
-
"@types/node": "^
|
|
101
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
102
|
-
"@typescript-eslint/parser": "^
|
|
100
|
+
"@sinonjs/fake-timers": "^9.1.0",
|
|
101
|
+
"@types/node": "^17.0.15",
|
|
102
|
+
"@typescript-eslint/eslint-plugin": "^5.10.2",
|
|
103
|
+
"@typescript-eslint/parser": "^5.10.2",
|
|
103
104
|
"concat-stream": "^2.0.0",
|
|
104
|
-
"duplexify": "^4.1.
|
|
105
|
+
"duplexify": "^4.1.2",
|
|
105
106
|
"license-checker": "^25.0.1",
|
|
106
|
-
"markdownlint-cli": "^0.
|
|
107
|
-
"mqtt": "^4.
|
|
107
|
+
"markdownlint-cli": "^0.31.0",
|
|
108
|
+
"mqtt": "^4.3.4",
|
|
108
109
|
"mqtt-connection": "^4.1.0",
|
|
109
110
|
"pre-commit": "^1.2.2",
|
|
110
111
|
"proxyquire": "^2.1.3",
|
|
111
|
-
"release-it": "^14.
|
|
112
|
+
"release-it": "^14.12.4",
|
|
112
113
|
"snazzy": "^9.0.0",
|
|
113
|
-
"standard": "^16.0.
|
|
114
|
-
"tap": "^15.
|
|
115
|
-
"tsd": "^0.
|
|
116
|
-
"typescript": "^4.
|
|
114
|
+
"standard": "^16.0.4",
|
|
115
|
+
"tap": "^15.1.6",
|
|
116
|
+
"tsd": "^0.19.1",
|
|
117
|
+
"typescript": "^4.5.5",
|
|
117
118
|
"websocket-stream": "^5.5.2"
|
|
118
119
|
},
|
|
119
120
|
"dependencies": {
|
|
@@ -122,11 +123,11 @@
|
|
|
122
123
|
"bulk-write-stream": "^2.0.1",
|
|
123
124
|
"end-of-stream": "^1.4.4",
|
|
124
125
|
"fastfall": "^1.5.1",
|
|
125
|
-
"fastparallel": "^2.4.
|
|
126
|
+
"fastparallel": "^2.4.1",
|
|
126
127
|
"fastseries": "^2.0.0",
|
|
127
|
-
"hyperid": "^
|
|
128
|
-
"mqemitter": "^4.
|
|
129
|
-
"mqtt-packet": "^
|
|
128
|
+
"hyperid": "^3.0.0",
|
|
129
|
+
"mqemitter": "^4.5.0",
|
|
130
|
+
"mqtt-packet": "^7.1.2",
|
|
130
131
|
"readable-stream": "^3.6.0",
|
|
131
132
|
"retimer": "^3.0.0",
|
|
132
133
|
"reusify": "^1.0.4",
|
package/test/auth.js
CHANGED
|
@@ -761,7 +761,7 @@ test('negate subscription', function (t) {
|
|
|
761
761
|
})
|
|
762
762
|
|
|
763
763
|
test('negate multiple subscriptions', function (t) {
|
|
764
|
-
t.plan(
|
|
764
|
+
t.plan(6)
|
|
765
765
|
|
|
766
766
|
const s = connect(setup())
|
|
767
767
|
t.teardown(s.broker.close.bind(s.broker))
|
|
@@ -771,6 +771,18 @@ test('negate multiple subscriptions', function (t) {
|
|
|
771
771
|
cb(null, null)
|
|
772
772
|
}
|
|
773
773
|
|
|
774
|
+
const expectedSubs = [{
|
|
775
|
+
topic: 'hello',
|
|
776
|
+
qos: 128
|
|
777
|
+
}, {
|
|
778
|
+
topic: 'world',
|
|
779
|
+
qos: 128
|
|
780
|
+
}]
|
|
781
|
+
|
|
782
|
+
s.broker.once('subscribe', function (subs, client) {
|
|
783
|
+
t.same(subs, expectedSubs)
|
|
784
|
+
})
|
|
785
|
+
|
|
774
786
|
s.inStream.write({
|
|
775
787
|
cmd: 'subscribe',
|
|
776
788
|
messageId: 24,
|
package/test/basic.js
CHANGED
|
@@ -95,6 +95,23 @@ test('publish empty topic throws error', function (t) {
|
|
|
95
95
|
})
|
|
96
96
|
})
|
|
97
97
|
|
|
98
|
+
test('publish to $SYS topic throws error', function (t) {
|
|
99
|
+
t.plan(1)
|
|
100
|
+
|
|
101
|
+
const s = connect(setup())
|
|
102
|
+
t.teardown(s.broker.close.bind(s.broker))
|
|
103
|
+
|
|
104
|
+
s.inStream.write({
|
|
105
|
+
cmd: 'publish',
|
|
106
|
+
topic: '$SYS/not/allowed',
|
|
107
|
+
payload: 'world'
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
s.broker.on('clientError', function (client, err) {
|
|
111
|
+
t.pass('should emit error')
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
|
|
98
115
|
;[{ qos: 0, clean: false }, { qos: 0, clean: true }, { qos: 1, clean: false }, { qos: 1, clean: true }].forEach(function (ele) {
|
|
99
116
|
test('subscribe a single topic in QoS ' + ele.qos + ' [clean=' + ele.clean + ']', function (t) {
|
|
100
117
|
t.plan(5)
|
|
@@ -243,6 +260,35 @@ test('subscribe should have messageId', function (t) {
|
|
|
243
260
|
})
|
|
244
261
|
})
|
|
245
262
|
|
|
263
|
+
test('subscribe with messageId 0 should return suback', function (t) {
|
|
264
|
+
t.plan(1)
|
|
265
|
+
|
|
266
|
+
const s = connect(setup())
|
|
267
|
+
t.teardown(s.broker.close.bind(s.broker))
|
|
268
|
+
|
|
269
|
+
s.inStream.write({
|
|
270
|
+
cmd: 'subscribe',
|
|
271
|
+
subscriptions: [{
|
|
272
|
+
topic: 'hello',
|
|
273
|
+
qos: 0
|
|
274
|
+
}],
|
|
275
|
+
messageId: 0
|
|
276
|
+
})
|
|
277
|
+
s.outStream.once('data', function (packet) {
|
|
278
|
+
t.same(packet, {
|
|
279
|
+
cmd: 'suback',
|
|
280
|
+
messageId: 0,
|
|
281
|
+
dup: false,
|
|
282
|
+
length: 3,
|
|
283
|
+
qos: 0,
|
|
284
|
+
retain: false,
|
|
285
|
+
granted: [
|
|
286
|
+
0
|
|
287
|
+
]
|
|
288
|
+
}, 'packet matches')
|
|
289
|
+
})
|
|
290
|
+
})
|
|
291
|
+
|
|
246
292
|
test('unsubscribe', function (t) {
|
|
247
293
|
t.plan(5)
|
|
248
294
|
|
package/test/qos1.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const { test } = require('tap')
|
|
4
4
|
const concat = require('concat-stream')
|
|
5
5
|
const { setup, connect, subscribe } = require('./helper')
|
|
6
|
+
const Faketimers = require('@sinonjs/fake-timers')
|
|
6
7
|
const aedes = require('../')
|
|
7
8
|
|
|
8
9
|
test('publish QoS 1', function (t) {
|
|
@@ -510,65 +511,48 @@ test('resend publish on non-clean reconnect QoS 1', function (t) {
|
|
|
510
511
|
})
|
|
511
512
|
|
|
512
513
|
test('resend many publish on non-clean reconnect QoS 1', function (t) {
|
|
513
|
-
t.plan(
|
|
514
|
-
|
|
514
|
+
t.plan(4)
|
|
515
515
|
const broker = aedes()
|
|
516
|
-
|
|
516
|
+
const clock = Faketimers.createClock()
|
|
517
|
+
|
|
518
|
+
t.teardown(() => {
|
|
519
|
+
broker.close.bind(broker)
|
|
520
|
+
clock.reset.bind(clock)
|
|
521
|
+
})
|
|
517
522
|
|
|
518
523
|
const opts = { clean: false, clientId: 'abcde' }
|
|
519
524
|
let subscriber = connect(setup(broker), opts)
|
|
525
|
+
const publisher = connect(setup(broker))
|
|
526
|
+
const { through } = require('../lib/utils')
|
|
527
|
+
const total = through().writableHighWaterMark * 2
|
|
528
|
+
|
|
529
|
+
let received = 0
|
|
530
|
+
clock.setTimeout(() => {
|
|
531
|
+
broker.close()
|
|
532
|
+
t.equal(received, total)
|
|
533
|
+
}, total)
|
|
520
534
|
|
|
521
535
|
subscribe(t, subscriber, 'hello', 1, function () {
|
|
522
536
|
subscriber.inStream.end()
|
|
523
537
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
let count = 0
|
|
527
|
-
while (++count <= 17) {
|
|
538
|
+
for (let sent = 0; sent < total; sent++) {
|
|
528
539
|
publisher.inStream.write({
|
|
529
540
|
cmd: 'publish',
|
|
530
541
|
topic: 'hello',
|
|
531
|
-
payload: 'message-' +
|
|
542
|
+
payload: 'message-' + sent,
|
|
532
543
|
qos: 1,
|
|
533
|
-
messageId: 42 +
|
|
544
|
+
messageId: 42 + sent
|
|
534
545
|
})
|
|
535
546
|
}
|
|
536
547
|
publisher.outStream.once('data', function (packet) {
|
|
537
|
-
t.equal(packet.cmd, 'puback')
|
|
538
|
-
|
|
539
548
|
subscriber = connect(setup(broker), opts)
|
|
540
|
-
|
|
541
|
-
const expected = [
|
|
542
|
-
[43, 'message-1'],
|
|
543
|
-
[44, 'message-2'],
|
|
544
|
-
[45, 'message-3'],
|
|
545
|
-
[46, 'message-4'],
|
|
546
|
-
[47, 'message-5'],
|
|
547
|
-
[48, 'message-6'],
|
|
548
|
-
[49, 'message-7'],
|
|
549
|
-
[50, 'message-8'],
|
|
550
|
-
[51, 'message-9'],
|
|
551
|
-
[52, 'message-10'],
|
|
552
|
-
[53, 'message-11'],
|
|
553
|
-
[54, 'message-12'],
|
|
554
|
-
[55, 'message-13'],
|
|
555
|
-
[56, 'message-14'],
|
|
556
|
-
[57, 'message-15'],
|
|
557
|
-
[58, 'message-16'],
|
|
558
|
-
[59, 'message-17']
|
|
559
|
-
]
|
|
560
|
-
|
|
561
|
-
let recievedCount = 0
|
|
562
549
|
subscriber.outStream.on('data', function (packet) {
|
|
563
550
|
subscriber.inStream.write({
|
|
564
551
|
cmd: 'puback',
|
|
565
552
|
messageId: packet.messageId
|
|
566
553
|
})
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
t.not(packet.messageId, messageId, 'messageId should not match')
|
|
571
|
-
t.same(packet.payload, Buffer.from(payload), 'payload should match')
|
|
554
|
+
received++
|
|
555
|
+
clock.tick(1)
|
|
572
556
|
})
|
|
573
557
|
})
|
|
574
558
|
})
|
package/test/retain.js
CHANGED
|
@@ -194,6 +194,43 @@ test('reconnected subscriber will not receive retained messages when QoS 0 and c
|
|
|
194
194
|
})
|
|
195
195
|
})
|
|
196
196
|
|
|
197
|
+
test('subscriber will not receive retained messages when QoS is 128', function (t) {
|
|
198
|
+
t.plan(3)
|
|
199
|
+
|
|
200
|
+
const broker = aedes()
|
|
201
|
+
t.teardown(broker.close.bind(broker))
|
|
202
|
+
|
|
203
|
+
const pubPacket = {
|
|
204
|
+
cmd: 'publish',
|
|
205
|
+
topic: 'hello',
|
|
206
|
+
payload: 'world',
|
|
207
|
+
qos: 1,
|
|
208
|
+
retain: true,
|
|
209
|
+
messageId: 42
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
broker.authorizeSubscribe = function (client, sub, callback) {
|
|
213
|
+
if (sub.topic === pubPacket.topic) {
|
|
214
|
+
callback(null, null)
|
|
215
|
+
} else {
|
|
216
|
+
callback(null, sub)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const publisher = connect(setup(broker), { clean: true })
|
|
221
|
+
|
|
222
|
+
publisher.inStream.write(pubPacket)
|
|
223
|
+
|
|
224
|
+
publisher.outStream.on('data', function (packet) {
|
|
225
|
+
const subscriber = connect(setup(broker), { clean: true })
|
|
226
|
+
subscribe(t, subscriber, pubPacket.topic, 128, function () {
|
|
227
|
+
subscriber.outStream.on('data', function (packet) {
|
|
228
|
+
t.fail('should not received retain message')
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
})
|
|
233
|
+
|
|
197
234
|
// [MQTT-3.3.1-6]
|
|
198
235
|
test('new QoS 0 subscribers receive QoS 0 retained messages when clean', function (t) {
|
|
199
236
|
t.plan(9)
|
package/types/client.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { IncomingMessage } from 'http'
|
|
2
2
|
import { PublishPacket, SubscribePacket, Subscription, Subscriptions, UnsubscribePacket } from './packet'
|
|
3
3
|
import { Connection } from './instance'
|
|
4
|
+
import { EventEmitter } from 'events'
|
|
4
5
|
|
|
5
|
-
export interface Client {
|
|
6
|
+
export interface Client extends EventEmitter {
|
|
6
7
|
id: Readonly<string>
|
|
7
8
|
clean: Readonly<boolean>
|
|
8
9
|
version: Readonly<number>
|
package/types/instance.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Duplex } from 'stream'
|
|
|
2
2
|
import { Socket } from 'net'
|
|
3
3
|
import { Client } from './client'
|
|
4
4
|
import type { AedesPublishPacket, ConnectPacket, ConnackPacket, Subscription, PingreqPacket, PublishPacket, PubrelPacket } from './packet'
|
|
5
|
+
import { EventEmitter } from 'events'
|
|
5
6
|
|
|
6
7
|
type LastHearthbeatTimestamp = Date;
|
|
7
8
|
|
|
@@ -12,7 +13,7 @@ export interface Brokers {
|
|
|
12
13
|
export type Connection = Duplex | Socket
|
|
13
14
|
|
|
14
15
|
/* eslint no-unused-vars: 0 */
|
|
15
|
-
export enum AuthErrorCode {
|
|
16
|
+
export const enum AuthErrorCode {
|
|
16
17
|
UNNACCEPTABLE_PROTOCOL = 1,
|
|
17
18
|
IDENTIFIER_REJECTED = 2,
|
|
18
19
|
SERVER_UNAVAILABLE = 3,
|
|
@@ -56,7 +57,7 @@ export interface AedesOptions {
|
|
|
56
57
|
published?: PublishedHandler
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
export interface Aedes {
|
|
60
|
+
export interface Aedes extends EventEmitter {
|
|
60
61
|
id: Readonly<string>
|
|
61
62
|
connectedClients: Readonly<number>
|
|
62
63
|
closed: Readonly<boolean>
|
|
@@ -89,4 +90,11 @@ export interface Aedes {
|
|
|
89
90
|
callback: () => void
|
|
90
91
|
): void
|
|
91
92
|
close (callback?: () => void): void
|
|
93
|
+
|
|
94
|
+
preConnect: PreConnectHandler
|
|
95
|
+
authenticate: AuthenticateHandler
|
|
96
|
+
authorizePublish: AuthorizePublishHandler
|
|
97
|
+
authorizeSubscribe: AuthorizeSubscribeHandler
|
|
98
|
+
authorizeForward: AuthorizeForwardHandler
|
|
99
|
+
published: PublishedHandler
|
|
92
100
|
}
|