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.
@@ -16,14 +16,14 @@ jobs:
16
16
 
17
17
  strategy:
18
18
  matrix:
19
- node-version: [10.x, 12.x, 14.x]
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.5
26
+ uses: actions/setup-node@v2.5.1
27
27
  with:
28
28
  node-version: ${{ matrix.node-version }}
29
29
 
package/.taprc CHANGED
@@ -2,4 +2,5 @@ ts: false
2
2
  jsx: false
3
3
  flow: false
4
4
  coverage: true
5
- strict: true
5
+ strict: true
6
+ check-coverage: false
package/README.md CHANGED
@@ -7,10 +7,8 @@
7
7
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/moscajs/aedes/pulls)\
8
8
  [![Total alerts](https://img.shields.io/lgtm/alerts/g/moscajs/aedes.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/moscajs/aedes/alerts/)
9
9
  [![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/moscajs/aedes.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/moscajs/aedes/context:javascript)
10
- [![Coverage Status](https://coveralls.io/repos/moscajs/aedes/badge.svg?branch=master&service=github)](https://coveralls.io/github/moscajs/aedes?branch=master)
10
+ [![Coverage Status](https://coveralls.io/repos/moscajs/aedes/badge.svg?branch=main&service=github)](https://coveralls.io/github/moscajs/aedes?branch=main)
11
11
  [![Known Vulnerabilities](https://snyk.io/test/github/moscajs/aedes/badge.svg)](https://snyk.io/test/github/moscajs/aedes)\
12
- [![Dependencies Status](https://david-dm.org/moscajs/aedes/status.svg)](https://david-dm.org/moscajs/aedes)
13
- [![devDependencies Status](https://david-dm.org/moscajs/aedes/dev-status.svg)](https://david-dm.org/moscajs/aedes?type=dev)\
14
12
  ![node](https://img.shields.io/node/v/aedes)
15
13
  [![NPM version](https://img.shields.io/npm/v/aedes.svg?style=flat)](https://www.npmjs.com/aedes)
16
14
  [![NPM downloads](https://img.shields.io/npm/dm/aedes.svg?style=flat)](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 = '$SYS/' + that.id + '/heartbeat'
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('$SYS/+/heartbeat', function storeBroker (packet, done) {
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('$SYS/+/new/clients', function closeSameClients (packet, done) {
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('$SYS') === 0) {
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: '$SYS/' + this.id + '/new/clients',
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: '$SYS/' + this.id + '/disconnect/clients',
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 thie event. `client` is `null` when `packet` is an internal message like aedes heartbeat message and LWT.
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.push(subs[i].topic)
203
- subs.qos = this.subState[i].granted
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: '$SYS/' + broker.id + '/new/subscribes',
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
- const persistence = broker.persistence
222
- const stream = persistence.createRetainedStreamCombi(topics)
223
- stream.pipe(through(function sendRetained (packet, enc, cb) {
224
- packet = new Packet({
225
- cmd: packet.cmd,
226
- qos: packet.qos,
227
- topic: packet.topic,
228
- payload: packet.payload,
229
- retain: true
230
- }, broker)
231
- // this should not be deduped
232
- packet.brokerId = null
233
- client.deliverQoS(packet, cb)
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: '$SYS/' + client.broker.id + '/new/unsubscribes',
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
@@ -39,5 +39,6 @@ function through (transform) {
39
39
 
40
40
  module.exports = {
41
41
  validateTopic,
42
- through
42
+ through,
43
+ $SYS_PREFIX: '$SYS/'
43
44
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aedes",
3
- "version": "0.45.2",
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": "^7.0.5",
100
- "@types/node": "^14.14.41",
101
- "@typescript-eslint/eslint-plugin": "^4.22.0",
102
- "@typescript-eslint/parser": "^4.22.0",
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.1",
105
+ "duplexify": "^4.1.2",
105
106
  "license-checker": "^25.0.1",
106
- "markdownlint-cli": "^0.27.1",
107
- "mqtt": "^4.2.6",
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.6.1",
112
+ "release-it": "^14.12.4",
112
113
  "snazzy": "^9.0.0",
113
- "standard": "^16.0.3",
114
- "tap": "^15.0.2",
115
- "tsd": "^0.14.0",
116
- "typescript": "^4.2.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.0",
126
+ "fastparallel": "^2.4.1",
126
127
  "fastseries": "^2.0.0",
127
- "hyperid": "^2.1.0",
128
- "mqemitter": "^4.4.1",
129
- "mqtt-packet": "^6.9.1",
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(5)
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(38)
514
-
514
+ t.plan(4)
515
515
  const broker = aedes()
516
- t.teardown(broker.close.bind(broker))
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
- const publisher = connect(setup(broker))
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-' + count,
542
+ payload: 'message-' + sent,
532
543
  qos: 1,
533
- messageId: 42 + count
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
- const [messageId, payload] = expected[recievedCount++]
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>
@@ -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
  }