Repository: moscajs/aedes
Pull request #930 (2024-01-12 14:04:52)

feat: add `opts.keepaliveLimit`
Hi,

this is a PR for this discussion: https://github.com/moscajs/aedes/discussions/865 


coveralls: ## Pull Request Test Coverage Report for [Build 7503193309](https://coveralls.io/builds/65041638)


* **0** of **0**   changed or added relevant lines in **0** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.002%**) to **99.741%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/65041638/badge)](https://coveralls.io/builds/65041638) |
| :-- | --: |
| Change from base [Build 7207966564](https://coveralls.io/builds/64564431): |  0.002% |
| Covered Lines: | 820 |
| Relevant Lines: | 820 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/aedes.js b/aedes.js
index c02d289..fd4f9b7 100644
--- a/aedes.js
+++ b/aedes.js
@@ -29,7 +29,8 @@ const defaultOptions = {
   trustProxy: false,
   trustedProxies: [],
   queueLimit: 42,
-  maxClientsIdLength: 23
+  maxClientsIdLength: 23,
+  keepaliveLimit: 0
 }
 
 function Aedes (opts) {
@@ -47,6 +48,7 @@ function Aedes (opts) {
   this.counter = 0
   this.queueLimit = opts.queueLimit
   this.connectTimeout = opts.connectTimeout
+  this.keepaliveLimit = opts.keepaliveLimit
   this.maxClientsIdLength = opts.maxClientsIdLength
   this.mq = opts.mq || mqemitter({
     concurrency: opts.concurrency,
diff --git a/docs/Aedes.md b/docs/Aedes.md
index 67f15e7..46bc4dc 100644
--- a/docs/Aedes.md
+++ b/docs/Aedes.md
@@ -42,6 +42,7 @@
   - `heartbeatInterval` `<number>` an interval in millisconds at which server beats its health signal in `$SYS/<aedes.id>/heartbeat` topic. __Default__: `60000`
   - `id` `<string>` aedes broker unique identifier. __Default__: `uuidv4()`
   - `connectTimeout` `<number>` maximum waiting time in milliseconds waiting for a [`CONNECT`][CONNECT] packet. __Default__: `30000`
+  - `keepaliveLimit` `<number>` maximum client keep alive time allowed, 0 means no limit. __Default__: `0`
 - Returns `<Aedes>`
 
 Create a new Aedes server.
diff --git a/lib/handlers/connect.js b/lib/handlers/connect.js
index a4c32d0..6e27e6a 100644
--- a/lib/handlers/connect.js
+++ b/lib/handlers/connect.js
@@ -36,7 +36,8 @@ const errorMessages = [
   'identifier rejected',
   'Server unavailable',
   'bad user name or password',
-  'not authorized'
+  'not authorized',
+  'keep alive limit exceeded'
 ]
 
 function handleConnect (client, packet, done) {
@@ -66,9 +67,14 @@ function init (client, packet, done) {
   if (packet.protocolVersion === 3 && clientId.length > client.broker.maxClientsIdLength) {
     returnCode = 2
   }
+  // check if the client keepalive is compatible with broker settings
+  if (client.broker.keepaliveLimit && (!packet.keepalive || packet.keepalive > client.broker.keepaliveLimit)) {
+    returnCode = 6
+  }
   if (returnCode > 0) {
     const error = new Error(errorMessages[returnCode])
     error.errorCode = returnCode
+    console.error(error)
     doConnack(
       { client, returnCode, sessionPresent: false },
       done.bind(this, error))
diff --git a/types/instance.d.ts b/types/instance.d.ts
index f912a95..22bd3ae 100644
--- a/types/instance.d.ts
+++ b/types/instance.d.ts
@@ -75,6 +75,7 @@ export interface AedesOptions {
   concurrency?: number;
   heartbeatInterval?: number;
   connectTimeout?: number;
+  keepaliveLimit?: number;
   queueLimit?: number;
   maxClientsIdLength?: number;
   preConnect?: PreConnectHandler;


Test patch:
diff --git a/test/connect.js b/test/connect.js
index d160738..fb6de7f 100644
--- a/test/connect.js
+++ b/test/connect.js
@@ -97,6 +97,40 @@ test('reject client requested for unsupported protocol version', function (t) {
   })
 })
 
+test('reject clients that exceed the keepalive limit', function (t) {
+  t.plan(3)
+
+  const broker = aedes({
+    keepaliveLimit: 100
+  })
+  t.teardown(broker.close.bind(broker))
+
+  const s = setup(broker)
+
+  s.inStream.write({
+    cmd: 'connect',
+    keepalive: 150
+  })
+  s.outStream.on('data', function (packet) {
+    console.log(packet)
+    t.same(packet, {
+      cmd: 'connack',
+      returnCode: 6,
+      length: 2,
+      qos: 0,
+      retain: false,
+      dup: false,
+      topic: null,
+      payload: null,
+      sessionPresent: false
+    }, 'unsuccessful connack, keep alive limit exceeded')
+  })
+  broker.on('connectionError', function (client, err) {
+    t.equal(err.message, 'keep alive limit exceeded')
+    t.equal(broker.connectedClients, 0)
+  })
+})
+
 // Guarded in mqtt-packet
 test('reject clients with no clientId running on MQTT 3.1.0', function (t) {
   t.plan(3)
diff --git a/test/types/aedes.test-d.ts b/test/types/aedes.test-d.ts
index 744ee3d..cc14591 100644
--- a/test/types/aedes.test-d.ts
+++ b/test/types/aedes.test-d.ts
@@ -23,6 +23,7 @@ broker = new Aedes({
   heartbeatInterval: 60000,
   connectTimeout: 30000,
   maxClientsIdLength: 23,
+  keepaliveLimit: 0,
   preConnect: (client: Client, packet: ConnectPacket, callback) => {
     if (client.req) {
       callback(new Error('not websocket stream'), false)