aedes 0.46.3 → 0.47.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.
@@ -16,14 +16,14 @@ jobs:
16
16
 
17
17
  strategy:
18
18
  matrix:
19
- node-version: [12.x, 14.x, 16.x]
19
+ node-version: [14.x, 16.x, "*"]
20
20
  os: [ubuntu-latest, windows-latest, macOS-latest]
21
21
 
22
22
  steps:
23
- - uses: actions/checkout@v2.4.0
23
+ - uses: actions/checkout@v3
24
24
 
25
25
  - name: Use Node.js
26
- uses: actions/setup-node@v2.5.1
26
+ uses: actions/setup-node@v3
27
27
  with:
28
28
  node-version: ${{ matrix.node-version }}
29
29
 
package/README.md CHANGED
@@ -24,7 +24,7 @@ Barebone MQTT server that can run on any stream servers
24
24
  - [Features](#features)
25
25
  - [Examples](#examples)
26
26
  - [Clusters](#clusters)
27
- - [Exensions](#exensions)
27
+ - [Extensions](#extensions)
28
28
  - [Middleware Plugins](#middleware-plugins)
29
29
  - [Persistence](#persistence)
30
30
  - [MQEmitter](#mqemitter)
package/aedes.js CHANGED
@@ -5,14 +5,13 @@ const util = require('util')
5
5
  const parallel = require('fastparallel')
6
6
  const series = require('fastseries')
7
7
  const { v4: uuidv4 } = require('uuid')
8
- const bulk = require('bulk-write-stream')
9
8
  const reusify = require('reusify')
10
- const { pipeline } = require('readable-stream')
9
+ const { pipeline } = require('stream')
11
10
  const Packet = require('aedes-packet')
12
11
  const memory = require('aedes-persistence')
13
12
  const mqemitter = require('mqemitter')
14
13
  const Client = require('./lib/client')
15
- const { $SYS_PREFIX } = require('./lib/utils')
14
+ const { $SYS_PREFIX, bulk } = require('./lib/utils')
16
15
 
17
16
  module.exports = Aedes.Server = Aedes
18
17
 
@@ -102,7 +101,7 @@ function Aedes (opts) {
102
101
 
103
102
  pipeline(
104
103
  that.persistence.streamWill(that.brokers),
105
- bulk.obj(receiveWills),
104
+ bulk(receiveWills),
106
105
  function done (err) {
107
106
  if (err) {
108
107
  that.emit('error', err)
@@ -171,6 +170,7 @@ function storeRetained (packet, done) {
171
170
  }
172
171
 
173
172
  function emitPacket (packet, done) {
173
+ if (this.client) packet.clientId = this.client.id
174
174
  this.broker.mq.emit(packet, done)
175
175
  }
176
176
 
package/lib/client.js CHANGED
@@ -10,7 +10,7 @@ const QoSPacket = require('./qos-packet')
10
10
  const handleSubscribe = require('./handlers/subscribe')
11
11
  const handleUnsubscribe = require('./handlers/unsubscribe')
12
12
  const handle = require('./handlers')
13
- const { pipeline } = require('readable-stream')
13
+ const { pipeline } = require('stream')
14
14
  const { through } = require('./utils')
15
15
 
16
16
  module.exports = Client
@@ -89,9 +89,20 @@ function Client (broker, conn, req) {
89
89
  conn.on('end', this.close.bind(this))
90
90
  this._eos = eos(this.conn, this.close.bind(this))
91
91
 
92
- this.deliver0 = function deliverQoS0 (_packet, cb) {
92
+ const getToForwardPacket = (_packet) => {
93
+ // Mqttv5 3.8.3.1: https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html#_Toc3901169
94
+ // prevent to forward messages sent by the same client when no-local flag is set
95
+ if (_packet.clientId === that.id && _packet.nl) return
96
+
93
97
  const toForward = dedupe(that, _packet) &&
94
98
  that.broker.authorizeForward(that, _packet)
99
+
100
+ return toForward
101
+ }
102
+
103
+ this.deliver0 = function deliverQoS0 (_packet, cb) {
104
+ const toForward = getToForwardPacket(_packet)
105
+
95
106
  if (toForward) {
96
107
  // Give nodejs some time to clear stacks, or we will see
97
108
  // "Maximum call stack size exceeded" in a very high load
@@ -114,8 +125,8 @@ function Client (broker, conn, req) {
114
125
  that.deliver0(_packet, cb)
115
126
  return
116
127
  }
117
- const toForward = dedupe(that, _packet) &&
118
- that.broker.authorizeForward(that, _packet)
128
+ const toForward = getToForwardPacket(_packet)
129
+
119
130
  if (toForward) {
120
131
  setImmediate(() => {
121
132
  const packet = new QoSPacket(toForward, that)
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const retimer = require('retimer')
4
- const { pipeline } = require('readable-stream')
4
+ const { pipeline } = require('stream')
5
5
  const write = require('../write')
6
6
  const QoSPacket = require('../qos-packet')
7
7
  const { through } = require('../utils')
@@ -142,13 +142,12 @@ function addSubs (sub, done) {
142
142
  const nl = this.nl
143
143
  let func = qos > 0 ? client.deliverQoS : client.deliver0
144
144
 
145
- if (!rap) {
146
- const deliverFunc = func
147
- func = function handlePacketSubscription (_packet, cb) {
148
- _packet = new Packet(_packet, broker)
149
- _packet.retain = false
150
- deliverFunc(_packet, cb)
151
- }
145
+ const deliverFunc = func
146
+ func = function handlePacketSubscription (_packet, cb) {
147
+ _packet = new Packet(_packet, broker)
148
+ _packet.nl = nl
149
+ if (!rap) _packet.retain = false
150
+ deliverFunc(_packet, cb)
152
151
  }
153
152
 
154
153
  // [MQTT-4.7.2-1]
package/lib/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { Transform } = require('readable-stream')
3
+ const { Transform, Writable } = require('stream')
4
4
 
5
5
  function validateTopic (topic, message) {
6
6
  const end = topic.length - 1
@@ -37,8 +37,18 @@ function through (transform) {
37
37
  })
38
38
  }
39
39
 
40
+ function bulk (fn) {
41
+ return new Writable({
42
+ objectMode: true,
43
+ writev: function (chunks, cb) {
44
+ fn(chunks.map(chunk => chunk.chunk), cb)
45
+ }
46
+ })
47
+ }
48
+
40
49
  module.exports = {
41
50
  validateTopic,
42
51
  through,
52
+ bulk,
43
53
  $SYS_PREFIX: '$SYS/'
44
54
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aedes",
3
- "version": "0.46.3",
3
+ "version": "0.47.0",
4
4
  "description": "Stream-based MQTT broker",
5
5
  "main": "aedes.js",
6
6
  "types": "aedes.d.ts",
@@ -94,41 +94,39 @@
94
94
  },
95
95
  "homepage": "https://github.com/moscajs/aedes#readme",
96
96
  "engines": {
97
- "node": ">=12"
97
+ "node": ">=14"
98
98
  },
99
99
  "devDependencies": {
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",
100
+ "@sinonjs/fake-timers": "^9.1.2",
101
+ "@types/node": "^17.0.24",
102
+ "@typescript-eslint/eslint-plugin": "^5.19.0",
103
+ "@typescript-eslint/parser": "^5.19.0",
104
104
  "concat-stream": "^2.0.0",
105
105
  "duplexify": "^4.1.2",
106
106
  "license-checker": "^25.0.1",
107
- "markdownlint-cli": "^0.31.0",
108
- "mqtt": "^4.3.4",
107
+ "markdownlint-cli": "^0.31.1",
108
+ "mqtt": "^4.3.7",
109
109
  "mqtt-connection": "^4.1.0",
110
110
  "pre-commit": "^1.2.2",
111
111
  "proxyquire": "^2.1.3",
112
- "release-it": "^14.12.4",
112
+ "release-it": "^14.14.2",
113
113
  "snazzy": "^9.0.0",
114
114
  "standard": "^16.0.4",
115
- "tap": "^15.1.6",
116
- "tsd": "^0.19.1",
117
- "typescript": "^4.5.5",
115
+ "tap": "^16.0.1",
116
+ "tsd": "^0.20.0",
117
+ "typescript": "^4.6.3",
118
118
  "websocket-stream": "^5.5.2"
119
119
  },
120
120
  "dependencies": {
121
- "aedes-packet": "^2.3.1",
122
- "aedes-persistence": "^8.1.3",
123
- "bulk-write-stream": "^2.0.1",
121
+ "aedes-packet": "^3.0.0",
122
+ "aedes-persistence": "^9.1.1",
124
123
  "end-of-stream": "^1.4.4",
125
124
  "fastfall": "^1.5.1",
126
125
  "fastparallel": "^2.4.1",
127
126
  "fastseries": "^2.0.0",
128
- "hyperid": "^3.0.0",
127
+ "hyperid": "^3.0.1",
129
128
  "mqemitter": "^4.5.0",
130
129
  "mqtt-packet": "^7.1.2",
131
- "readable-stream": "^3.6.0",
132
130
  "retimer": "^3.0.0",
133
131
  "reusify": "^1.0.4",
134
132
  "uuid": "^8.3.2"
package/test/auth.js CHANGED
@@ -422,7 +422,7 @@ test('authentication error when non numeric return code is passed', function (t)
422
422
  test('authorize publish', function (t) {
423
423
  t.plan(4)
424
424
 
425
- const s = connect(setup())
425
+ const s = connect(setup(), { clientId: 'my-client-xyz' })
426
426
  t.teardown(s.broker.close.bind(s.broker))
427
427
 
428
428
  const expected = {
@@ -445,6 +445,7 @@ test('authorize publish', function (t) {
445
445
  t.notOk(Object.prototype.hasOwnProperty.call(packet, 'messageId'), 'should not contain messageId in QoS 0')
446
446
  expected.brokerId = s.broker.id
447
447
  expected.brokerCounter = s.broker.counter
448
+ expected.clientId = 'my-client-xyz'
448
449
  delete expected.length
449
450
  t.same(packet, expected, 'packet matches')
450
451
  cb()
@@ -460,7 +461,7 @@ test('authorize publish', function (t) {
460
461
  test('authorize waits for authenticate', function (t) {
461
462
  t.plan(6)
462
463
 
463
- const s = setup()
464
+ const s = setup(aedes({ clientId: 'my-client-xyz-2' }))
464
465
  t.teardown(s.broker.close.bind(s.broker))
465
466
 
466
467
  s.broker.authenticate = function (client, username, password, cb) {
@@ -485,7 +486,8 @@ test('authorize waits for authenticate', function (t) {
485
486
  qos: 0,
486
487
  retain: false,
487
488
  length: 12,
488
- dup: false
489
+ dup: false,
490
+ clientId: 'my-client'
489
491
  }
490
492
 
491
493
  s.broker.mq.on('hello', function (packet, cb) {
@@ -519,12 +521,13 @@ test('authorize publish from configOptions', function (t) {
519
521
  t.plan(4)
520
522
 
521
523
  const s = connect(setup(aedes({
524
+ clientId: 'my-client-xyz-3',
522
525
  authorizePublish: function (client, packet, cb) {
523
526
  t.ok(client, 'client exists')
524
527
  t.same(packet, expected, 'packet matches')
525
528
  cb()
526
529
  }
527
- })))
530
+ })), { clientId: 'my-client-xyz-3' })
528
531
  t.teardown(s.broker.close.bind(s.broker))
529
532
 
530
533
  const expected = {
@@ -541,6 +544,7 @@ test('authorize publish from configOptions', function (t) {
541
544
  t.notOk(Object.prototype.hasOwnProperty.call(packet, 'messageId'), 'should not contain messageId in QoS 0')
542
545
  expected.brokerId = s.broker.id
543
546
  expected.brokerCounter = s.broker.counter
547
+ expected.clientId = 'my-client-xyz-3'
544
548
  delete expected.length
545
549
  t.same(packet, expected, 'packet matches')
546
550
  cb()
@@ -589,7 +593,7 @@ test('do not authorize publish', function (t) {
589
593
  test('modify qos out of range in authorize publish ', function (t) {
590
594
  t.plan(2)
591
595
 
592
- const s = connect(setup())
596
+ const s = connect(setup(), { clientId: 'my-client-xyz-4' })
593
597
  t.teardown(s.broker.close.bind(s.broker))
594
598
 
595
599
  const expected = {
@@ -599,7 +603,8 @@ test('modify qos out of range in authorize publish ', function (t) {
599
603
  qos: 0,
600
604
  retain: false,
601
605
  length: 12,
602
- dup: false
606
+ dup: false,
607
+ clientId: 'my-client-xyz-4'
603
608
  }
604
609
 
605
610
  s.broker.authorizePublish = function (client, packet, cb) {
@@ -805,12 +810,19 @@ test('negate multiple subscriptions', function (t) {
805
810
  test('negate subscription with correct persistence', function (t) {
806
811
  t.plan(6)
807
812
 
813
+ // rh, rap, nl are undefined because mqtt.parser is set to MQTT 3.1.1 and will thus erase these props from s.inStream.write
808
814
  const expected = [{
809
815
  topic: 'hello',
810
- qos: 0
816
+ qos: 0,
817
+ rh: undefined,
818
+ rap: undefined,
819
+ nl: undefined
811
820
  }, {
812
821
  topic: 'world',
813
- qos: 0
822
+ qos: 0,
823
+ rh: undefined,
824
+ rap: undefined,
825
+ nl: undefined
814
826
  }]
815
827
 
816
828
  const broker = aedes()
@@ -839,10 +851,16 @@ test('negate subscription with correct persistence', function (t) {
839
851
  messageId: 24,
840
852
  subscriptions: [{
841
853
  topic: 'hello',
842
- qos: 0
854
+ qos: 0,
855
+ rh: 0,
856
+ rap: true,
857
+ nl: false
843
858
  }, {
844
859
  topic: 'world',
845
- qos: 0
860
+ qos: 0,
861
+ rh: 0,
862
+ rap: true,
863
+ nl: false
846
864
  }]
847
865
  })
848
866
  })
package/test/basic.js CHANGED
@@ -20,7 +20,7 @@ test('test aedes.Server', function (t) {
20
20
  test('publish QoS 0', function (t) {
21
21
  t.plan(2)
22
22
 
23
- const s = connect(setup())
23
+ const s = connect(setup(), { clientId: 'my-client-xyz-5' })
24
24
  t.teardown(s.broker.close.bind(s.broker))
25
25
 
26
26
  const expected = {
@@ -29,7 +29,8 @@ test('publish QoS 0', function (t) {
29
29
  payload: Buffer.from('world'),
30
30
  qos: 0,
31
31
  retain: false,
32
- dup: false
32
+ dup: false,
33
+ clientId: 'my-client-xyz-5'
33
34
  }
34
35
 
35
36
  s.broker.mq.on('hello', function (packet, cb) {
@@ -128,7 +129,7 @@ test('publish to $SYS topic throws error', function (t) {
128
129
  qos: 0,
129
130
  retain: false
130
131
  }
131
- const expectedSubs = ele.clean ? null : [{ topic: 'hello', qos: ele.qos }]
132
+ const expectedSubs = ele.clean ? null : [{ topic: 'hello', qos: ele.qos, rh: undefined, rap: undefined, nl: undefined }]
132
133
 
133
134
  subscribe(t, s, 'hello', ele.qos, function () {
134
135
  s.outStream.once('data', function (packet) {
@@ -188,7 +189,10 @@ test('return write errors to callback', function (t) {
188
189
  qos: 0,
189
190
  retain: false
190
191
  }
191
- const subs = [{ topic: 'hello', qos: ele.qos }, { topic: 'world', qos: ele.qos }]
192
+ const subs = [
193
+ { topic: 'hello', qos: ele.qos, rh: undefined, rap: undefined, nl: undefined },
194
+ { topic: 'world', qos: ele.qos, rh: undefined, rap: undefined, nl: undefined }
195
+ ]
192
196
  const expectedSubs = ele.clean ? null : subs
193
197
 
194
198
  subscribeMultiple(t, s, subs, [ele.qos, ele.qos], function () {
package/test/bridge.js ADDED
@@ -0,0 +1,57 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('tap')
4
+ const { setup, connect, subscribe } = require('./helper')
5
+
6
+ for (const qos of [0, 1, 2]) {
7
+ const packet = {
8
+ qos,
9
+ cmd: 'publish',
10
+ topic: 'hello',
11
+ payload: 'world'
12
+ }
13
+
14
+ if (qos > 0) packet.messageId = 42
15
+
16
+ test('normal client sends a publish message and shall receive it back, qos = ' + qos, function (t) {
17
+ const s = connect(setup())
18
+ t.teardown(s.broker.close.bind(s.broker))
19
+
20
+ const handle = setTimeout(() => {
21
+ t.fail('did not receive packet back')
22
+ t.end()
23
+ }, 1000)
24
+
25
+ subscribe(t, s, 'hello', qos, function () {
26
+ s.outStream.on('data', (packet) => {
27
+ if (packet.cmd === 'publish') {
28
+ clearTimeout(handle)
29
+ t.end()
30
+ } else if (packet.cmd === 'pubrec') {
31
+ s.inStream.write({ cmd: 'pubrel', messageId: 42 })
32
+ }
33
+ })
34
+
35
+ s.inStream.write(packet)
36
+ })
37
+ })
38
+
39
+ test('bridge client sends a publish message but shall not receive it back, qos = ' + qos, function (t) {
40
+ // protocolVersion 128 + 4 means mqtt 3.1.1 with bridgeMode enabled
41
+ // https://github.com/mqttjs/mqtt-packet/blob/7f7c2ed8bcb4b2c582851d120a94e0b4a731f661/parser.js#L171
42
+ const s = connect(setup(), { clientId: 'my-client-bridge-1', protocolVersion: 128 + 4 })
43
+ t.teardown(s.broker.close.bind(s.broker))
44
+
45
+ const handle = setTimeout(() => t.end(), 1000)
46
+
47
+ subscribe(t, s, 'hello', qos, function () {
48
+ s.outStream.on('data', function () {
49
+ clearTimeout(handle)
50
+ t.fail('should not receive packet back')
51
+ t.end()
52
+ })
53
+
54
+ s.inStream.write(packet)
55
+ })
56
+ })
57
+ }
@@ -905,10 +905,10 @@ test('should not receive a message on negated subscription', function (t) {
905
905
  test('programmatically add custom subscribe', function (t) {
906
906
  t.plan(6)
907
907
 
908
- const broker = aedes()
908
+ const broker = aedes({ clientId: 'my-client-xyz-7' })
909
909
  t.teardown(broker.close.bind(broker))
910
910
 
911
- const s = connect(setup(broker))
911
+ const s = connect(setup(broker), { clientId: 'my-client-xyz-7' })
912
912
  const expected = {
913
913
  cmd: 'publish',
914
914
  topic: 'hello',
@@ -924,7 +924,8 @@ test('programmatically add custom subscribe', function (t) {
924
924
  payload: Buffer.from('world'),
925
925
  qos: 0,
926
926
  retain: false,
927
- dup: false
927
+ dup: false,
928
+ clientId: 'my-client-xyz-7'
928
929
  }
929
930
  subscribe(t, s, 'hello', 0, function () {
930
931
  broker.subscribe('hello', deliver, function () {
@@ -963,9 +964,10 @@ test('custom function in broker.subscribe', function (t) {
963
964
  qos: 1,
964
965
  retain: false,
965
966
  dup: false,
966
- messageId: undefined
967
+ messageId: undefined,
968
+ clientId: 'my-client-xyz-6'
967
969
  }
968
- connect(s, {}, function () {
970
+ connect(s, { clientId: 'my-client-xyz-6' }, function () {
969
971
  broker.subscribe('hello', deliver, function () {
970
972
  t.pass('subscribed')
971
973
  })
package/test/qos2.js CHANGED
@@ -239,7 +239,7 @@ test('call published method with client with QoS 2', function (t) {
239
239
  t.teardown(broker.close.bind(broker))
240
240
 
241
241
  const opts = { clean: cleanSession }
242
- const publisher = connect(setup(broker))
242
+ const publisher = connect(setup(broker), { clientId: 'my-client-xyz-8' })
243
243
  const subscriber = connect(setup(broker), { ...opts, clientId: 'abcde' })
244
244
  const forwarded = {
245
245
  cmd: 'publish',
@@ -248,7 +248,8 @@ test('call published method with client with QoS 2', function (t) {
248
248
  qos: 2,
249
249
  retain: false,
250
250
  dup: false,
251
- messageId: undefined
251
+ messageId: undefined,
252
+ clientId: 'my-client-xyz-8'
252
253
  }
253
254
  const expected = {
254
255
  cmd: 'publish',
@@ -262,6 +263,7 @@ test('call published method with client with QoS 2', function (t) {
262
263
  broker.authorizeForward = function (client, packet) {
263
264
  forwarded.brokerId = broker.id
264
265
  forwarded.brokerCounter = broker.counter
266
+ delete packet.nl
265
267
  t.same(packet, forwarded, 'forwarded packet must match')
266
268
  return packet
267
269
  }
package/types/packet.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { AedesPacket } from 'aedes-packet'
2
2
  import { IConnackPacket, IConnectPacket, IPingreqPacket, IPublishPacket, IPubrelPacket, ISubscribePacket, ISubscription, IUnsubscribePacket } from 'mqtt-packet'
3
+ import { Client } from './client'
3
4
 
4
5
  export type SubscribePacket = ISubscribePacket & { cmd: 'subscribe' }
5
6
  export type UnsubscribePacket = IUnsubscribePacket & { cmd: 'unsubscribe' }
6
- export type Subscription = ISubscription & { clientId?: string }
7
+ export type Subscription = ISubscription & { clientId?: Client['id'] }
7
8
  export type Subscriptions = { subscriptions: Subscription[] }
8
9
 
9
10
  export type PublishPacket = IPublishPacket & { cmd: 'publish' }