ac-ratelimiter 2.0.0 → 2.0.2

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/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ <a name="2.0.2"></a>
2
+
3
+ ## [2.0.2](https://github.com/admiralcloud/ac-ratelimiter/compare/v2.0.1..v2.0.2) (2024-07-06 18:51:38)
4
+
5
+
6
+ ### Bug Fix
7
+
8
+ * **Limiter:** Replaced isPrivateIP with isSpecialIP | MP | [d14140a22a00e5a18eb21d286712f21f8daea8f3](https://github.com/admiralcloud/ac-ratelimiter/commit/d14140a22a00e5a18eb21d286712f21f8daea8f3)
9
+ Replaced isPrivateIP with isSpecialIP
10
+ Related issues: [undefined/undefined#master](undefined/browse/master)
11
+ <a name="2.0.1"></a>
12
+
13
+ ## [2.0.1](https://github.com/admiralcloud/ac-ratelimiter/compare/v2.0.0..v2.0.1) (2024-07-06 14:32:00)
14
+
15
+
16
+ ### Bug Fix
17
+
18
+ * **Limiter:** Updated packages | MP | [6d31ce52d1b300021ba471c3a6261859604ceec5](https://github.com/admiralcloud/ac-ratelimiter/commit/6d31ce52d1b300021ba471c3a6261859604ceec5)
19
+ Updated packages. Use ACError package. Fixed tests for updated packages
20
+ Related issues: [undefined/undefined#develop](undefined/browse/develop)
1
21
  <a name="2.0.0"></a>
2
22
 
3
23
  # [2.0.0](https://github.com/admiralcloud/ac-ratelimiter/compare/v1.0.10..v2.0.0) (2024-02-26 07:51:24)
package/index.js CHANGED
@@ -2,23 +2,7 @@ const { setTimeout } = require('timers/promises')
2
2
 
3
3
  const NodeCache = require('node-cache')
4
4
  const acts = require('ac-ip')
5
-
6
- class ACError extends Error {
7
- constructor(message, options = {}) {
8
- super(message)
9
-
10
- if (Error.captureStackTrace) {
11
- Error.captureStackTrace(this, ACError)
12
- }
13
- // info
14
- this.code = options.code || -1
15
- this.errorMessage = message
16
- // show other properties of options object
17
- for (const [key, value] of Object.entries(options)) {
18
- this[key] = value;
19
- }
20
- }
21
- }
5
+ const { ACError } = require('ac-custom-error')
22
6
 
23
7
  class RateLimiter {
24
8
  constructor({ redisInstance, logger = console, routes = [], knownIPs = [], ignorePrivateIps } = {}) {
@@ -76,7 +60,7 @@ class RateLimiter {
76
60
  rateLimitCounter
77
61
  }) {
78
62
 
79
- if (this.ignorePrivateIps && acts.isPrivate(ip)) return
63
+ if (this.ignorePrivateIps && acts.isSpecialIP(ip)) return
80
64
 
81
65
  const logIdentifier = typeof identifier === 'string' && identifier.replace(/(\w{1,4})-(\w{1,4})/g, 'xxxx')
82
66
  const knownIP = this.knownIPs.find(({ knownIP }) => knownIP === ip)
@@ -115,7 +99,7 @@ class RateLimiter {
115
99
  })
116
100
  }
117
101
 
118
- let current = {
102
+ const current = {
119
103
  expires,
120
104
  limit,
121
105
  throttleLimit,
@@ -145,7 +129,7 @@ class RateLimiter {
145
129
  else {
146
130
  // use Node cache (memory) for rate limiting - please see README before using in production!
147
131
  rateLimitCounter = this.cache.get(rateLimiterKey)
148
- let ts = this.cache.getTtl(rateLimiterKey) // ts in ms when the key will expire
132
+ const ts = this.cache.getTtl(rateLimiterKey) // ts in ms when the key will expire
149
133
  rateLimitCounter = rateLimitCounter + 1 || 1
150
134
  if (ts === undefined || ts === 0) {
151
135
  // first entry - set expiration
@@ -167,7 +151,7 @@ class RateLimiter {
167
151
  if (rateLimitCounter === current.limit || rateLimitCounter % 10 === 0) {
168
152
  rateLogger({ type: 'Blocking', rateLimitCounter, currentLimit: current.limit })
169
153
  }
170
- throw new ACError('tooManyRequestsFromThisIP', { status: 429, logging: false, counter: rateLimitCounter, additionalInfo: { expires: current.expires } })
154
+ throw new ACError('tooManyRequestsFromThisIP', 429, { logging: false, counter: rateLimitCounter, expires: current.expires })
171
155
 
172
156
  }
173
157
  else if (current.throttleLimit && rateLimitCounter > current.limit * 0.9) {
@@ -176,7 +160,7 @@ class RateLimiter {
176
160
  await setTimeout(current.expires * 1000)
177
161
  // do not "taint" process time when deliberately throttline
178
162
  if (req._startTime) req._startTime += current.expires * 1000
179
- throw new ACError('finalThrottlingActive_requestsIsDelayed', { status: 900, additionalInfo: { counter: rateLimitCounter, expires: current.expires } })
163
+ throw new ACError('finalThrottlingActive_requestsIsDelayed', 900, { counter: rateLimitCounter, expires: current.expires })
180
164
  }
181
165
  else if (current.throttleLimit && rateLimitCounter > current.throttleLimit) {
182
166
  // log the first throttling and every 50th entry
@@ -186,7 +170,7 @@ class RateLimiter {
186
170
  await setTimeout(current.delay)
187
171
  // do not "taint" process time when deliberately throttline
188
172
  if (req._startTime) req._startTime += current.delay
189
- throw new ACError('throttlingActive_requestsIsDelayed', { status: 900, additionalInfo: { counter: rateLimitCounter, expires: current.expires } })
173
+ throw new ACError('throttlingActive_requestsIsDelayed', 900, { counter: rateLimitCounter, expires: current.expires })
190
174
  }
191
175
  }
192
176
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ac-ratelimiter",
3
3
  "description": "Simple ratelimiter for express",
4
- "version": "2.0.0",
4
+ "version": "2.0.2",
5
5
  "author": "Mark Poepping (www.admiralcloud.com)",
6
6
  "repository": {
7
7
  "type": "git",
@@ -9,22 +9,26 @@
9
9
  },
10
10
  "homepage": "https://www.admiralcloud.com",
11
11
  "dependencies": {
12
- "ac-ip": "^3.1.1",
12
+ "ac-custom-error": "^1.0.0",
13
+ "ac-ip": "^4.1.0",
13
14
  "node-cache": "^5.1.2"
14
15
  },
15
16
  "devDependencies": {
16
17
  "ac-semantic-release": "^0.4.2",
17
18
  "chai": "^4.4.1",
18
- "eslint": "8.57.0",
19
- "ioredis": "^5.3.2",
20
- "mocha": "^10.3.0"
19
+ "eslint": "9.6.0",
20
+ "ioredis": "^5.4.1",
21
+ "mocha": "^10.6.0"
21
22
  },
22
23
  "scripts": {
23
- "test": "./node_modules/.bin/mocha --slow 1000 --colors ./test/test.js || :",
24
- "test-local": "env TESTMODE=true ./node_modules/.bin/mocha --slow 1000 --colors ./test/test.js || :",
24
+ "test": "./node_modules/.bin/mocha --slow 1000 --colors --bail ./test/test.js || :",
25
+ "test-local": "env TESTMODE=true ./node_modules/.bin/mocha --slow 1000 --colors --bail ./test/test.js || :",
25
26
  "test-jenkins": "JUNIT_REPORT_PATH=./report.xml mocha --slow 1000 --colors --reporter mocha-jenkins-reporter --reporter-options junit_report_name='RATELIMITER'"
26
27
  },
27
28
  "engines": {
28
- "node": ">=16.0.0"
29
+ "node": ">=18.0.0"
30
+ },
31
+ "resolutions": {
32
+ "mocha/chokidar/braces": "^3.0.3"
29
33
  }
30
34
  }
package/test/test.js CHANGED
@@ -6,7 +6,7 @@ const ratelimiterModule = require('../index')
6
6
  const Redis = require('ioredis')
7
7
  const redis = new Redis()
8
8
 
9
- let req = {
9
+ const req = {
10
10
  options: {
11
11
  controller: 'user',
12
12
  action: 'find',
@@ -15,12 +15,12 @@ let req = {
15
15
  }
16
16
 
17
17
 
18
- let initOptions = {
18
+ const initOptions = {
19
19
  routes: [
20
20
  { route: 'user/find', throttleLimit: 1, limit: 2, expires: 3, delay: 250 },
21
21
  ]
22
22
  }
23
- let options = {}
23
+ const options = {}
24
24
 
25
25
  const ratelimiter = new ratelimiterModule(initOptions)
26
26
  const ratelimiterRedis = new ratelimiterModule({ redisInstance: redis, routes: initOptions.routes })
@@ -37,7 +37,7 @@ describe('Use NodeCache', () => {
37
37
  })
38
38
 
39
39
  it('should not trigger - req #1', async() => {
40
- let result = await ratelimiter.limiter(req, options)
40
+ const result = await ratelimiter.limiter(req, options)
41
41
  expect(result).eql(undefined)
42
42
  })
43
43
  it('should still not trigger - req #2 - but throw 900', async() => {
@@ -45,12 +45,13 @@ describe('Use NodeCache', () => {
45
45
  await ratelimiter.limiter(req, options)
46
46
  }
47
47
  catch(e) {
48
+ console.log(45, e)
48
49
  expect(e).to.have.property('message', 'finalThrottlingActive_requestsIsDelayed')
49
- expect(e).to.have.property('status', 900)
50
+ expect(e).to.have.property('code', 900)
50
51
  }
51
52
  })
52
53
  it('should not trigger - req #3 - rate limiter is reset', async() => {
53
- let result = await ratelimiter.limiter(req, options)
54
+ const result = await ratelimiter.limiter(req, options)
54
55
  expect(result).eql(undefined)
55
56
  })
56
57
  })
@@ -81,7 +82,7 @@ describe('Use NodeCache', () => {
81
82
  catch(e) {
82
83
  expect(e).to.be.an('error')
83
84
  expect(e).to.have.property('message', 'tooManyRequestsFromThisIP')
84
- expect(e).to.have.property('status', 429)
85
+ expect(e).to.have.property('code', 429)
85
86
  }
86
87
  })
87
88
  })
@@ -105,7 +106,7 @@ describe('Use NodeCache', () => {
105
106
 
106
107
  it('should not trigger req #1', async() => {
107
108
  req.determinedIP = '2.3.4.1'
108
- let result = await ratelimiter.limiter(req, options)
109
+ const result = await ratelimiter.limiter(req, options)
109
110
  expect(result).eql(undefined)
110
111
  })
111
112
  it('should still not trigger - delayed req #2', async() => {
@@ -114,7 +115,7 @@ describe('Use NodeCache', () => {
114
115
  }
115
116
  catch(e) {
116
117
  expect(e).to.have.property('message', 'finalThrottlingActive_requestsIsDelayed')
117
- expect(e).to.have.property('status', 900)
118
+ expect(e).to.have.property('code', 900)
118
119
  }
119
120
  })
120
121
  it('should trigger the limiter', async() => {
@@ -123,7 +124,7 @@ describe('Use NodeCache', () => {
123
124
  }
124
125
  catch(e) {
125
126
  expect(e).to.have.property('message', 'tooManyRequestsFromThisIP')
126
- expect(e).to.have.property('status', 429)
127
+ expect(e).to.have.property('code', 429)
127
128
  }
128
129
  })
129
130
  })
@@ -132,7 +133,7 @@ describe('Use NodeCache', () => {
132
133
  describe('Test section #4 - routes with clientId', function() {
133
134
  describe('RATE LIMITER TEST', function() {
134
135
  this.timeout(5000)
135
- let req = {
136
+ const req = {
136
137
  options: {
137
138
  controller: 'customer',
138
139
  action: 'find'
@@ -152,7 +153,7 @@ describe('Use NodeCache', () => {
152
153
  })
153
154
 
154
155
  it('should not trigger - req #1', async() => {
155
- let result = await ratelimiter.limiter(req, options)
156
+ const result = await ratelimiter.limiter(req, options)
156
157
  expect(result).eql(undefined)
157
158
  })
158
159
  it('should still not trigger but delay - req #2', async() => {
@@ -161,11 +162,11 @@ describe('Use NodeCache', () => {
161
162
  }
162
163
  catch(e) {
163
164
  expect(e).to.have.property('message', 'finalThrottlingActive_requestsIsDelayed')
164
- expect(e).to.have.property('status', 900)
165
+ expect(e).to.have.property('code', 900)
165
166
  }
166
167
  })
167
168
  it('should still not trigger as ratelimiter is reset', async() => {
168
- let result = await ratelimiter.limiter(req, options)
169
+ const result = await ratelimiter.limiter(req, options)
169
170
  expect(result).eql(undefined)
170
171
  })
171
172
 
@@ -179,17 +180,17 @@ describe('Use NodeCache', () => {
179
180
  })
180
181
 
181
182
  it('should not trigger #1', async() => {
182
- let result = await ratelimiter.limiter(req, options)
183
+ const result = await ratelimiter.limiter(req, options)
183
184
  expect(result).eql(undefined)
184
185
  })
185
186
 
186
187
  it('should not trigger #2', async() => {
187
- let result = await ratelimiter.limiter(req, options)
188
+ const result = await ratelimiter.limiter(req, options)
188
189
  expect(result).eql(undefined)
189
190
  })
190
191
 
191
192
  it('should not trigger #3', async() => {
192
- let result = await ratelimiter.limiter(req, options)
193
+ const result = await ratelimiter.limiter(req, options)
193
194
  expect(result).eql(undefined)
194
195
  })
195
196
 
@@ -199,7 +200,7 @@ describe('Use NodeCache', () => {
199
200
  }
200
201
  catch(e) {
201
202
  expect(e).to.have.property('message', 'throttlingActive_requestsIsDelayed')
202
- expect(e).to.have.property('status', 900)
203
+ expect(e).to.have.property('code', 900)
203
204
  }
204
205
  })
205
206
  })
@@ -208,7 +209,7 @@ describe('Use NodeCache', () => {
208
209
  describe('Test section #5 - no limiting', function () {
209
210
  describe('RATE LIMITER TEST', function() {
210
211
  this.timeout(5000)
211
- let req = {
212
+ const req = {
212
213
  options: {
213
214
  controller: 'search',
214
215
  action: 'search'
@@ -221,27 +222,27 @@ describe('Use NodeCache', () => {
221
222
  })
222
223
 
223
224
  it('should not trigger #1', async() => {
224
- let result = await ratelimiter.limiter(req, options)
225
+ const result = await ratelimiter.limiter(req, options)
225
226
  expect(result).eql(undefined)
226
227
  })
227
228
 
228
229
  it('should not trigger #2', async() => {
229
- let result = await ratelimiter.limiter(req, options)
230
+ const result = await ratelimiter.limiter(req, options)
230
231
  expect(result).eql(undefined)
231
232
  })
232
233
 
233
234
  it('should not trigger #3', async() => {
234
- let result = await ratelimiter.limiter(req, options)
235
+ const result = await ratelimiter.limiter(req, options)
235
236
  expect(result).eql(undefined)
236
237
  })
237
238
 
238
239
  it('should not trigger #4', async() => {
239
- let result = await ratelimiter.limiter(req, options)
240
+ const result = await ratelimiter.limiter(req, options)
240
241
  expect(result).eql(undefined)
241
242
  })
242
243
 
243
244
  it('should not trigger #5', async() => {
244
- let result = await ratelimiter.limiter(req, options)
245
+ const result = await ratelimiter.limiter(req, options)
245
246
  expect(result).eql(undefined)
246
247
  })
247
248
  })
@@ -251,7 +252,7 @@ describe('Use NodeCache', () => {
251
252
  describe('RATE LIMITER TEST - ignorePrivateIps', function() {
252
253
  this.timeout(5000)
253
254
 
254
- let req = {
255
+ const req = {
255
256
  options: {
256
257
  controller: 'user',
257
258
  action: 'find'
@@ -278,19 +279,19 @@ describe('Use NodeCache', () => {
278
279
  }
279
280
  catch(e) {
280
281
  expect(e).to.have.property('message', 'tooManyRequestsFromThisIP')
281
- expect(e).to.have.property('status', 429)
282
+ expect(e).to.have.property('code', 429)
282
283
  }
283
284
  })
284
285
 
285
286
  it('should not trigger', async() => {
286
287
  req.determinedIP = '127.0.0.1'
287
- let result = await ratelimiter.limiter(req, options)
288
+ const result = await ratelimiter.limiter(req, options)
288
289
  expect(result).eql(undefined)
289
290
  })
290
291
 
291
- it('should not trigger - IPv6', async() => {
292
- req.determinedIP = '::ffff:127.0.0.1'
293
- let result = await ratelimiter.limiter(req, options)
292
+ it('should not trigger - private IPv6', async() => {
293
+ req.determinedIP = 'FD8A:4C5D:3E1F:0001:ABCD:1234:5678:9ABC'
294
+ const result = await ratelimiter.limiter(req, options)
294
295
  expect(result).eql(undefined)
295
296
  })
296
297
  })
@@ -313,7 +314,7 @@ describe('Use NodeCache', () => {
313
314
  })
314
315
 
315
316
  it('should not trigger', async() => {
316
- let result = await ratelimiter.limiter(req, { rateLimitCounter: 0 })
317
+ const result = await ratelimiter.limiter(req, { rateLimitCounter: 0 })
317
318
  expect(result).eql(undefined)
318
319
  })
319
320
 
@@ -323,7 +324,7 @@ describe('Use NodeCache', () => {
323
324
  }
324
325
  catch(e) {
325
326
  expect(e).to.have.property('message', 'tooManyRequestsFromThisIP')
326
- expect(e).to.have.property('status', 429)
327
+ expect(e).to.have.property('code', 429)
327
328
  }
328
329
  })
329
330
 
@@ -353,13 +354,13 @@ describe('Use Redis', () => {
353
354
  it('should trigger immediately', async() => {
354
355
  req.determinedIP = '4.1.4.1'
355
356
  try {
356
- let x = await ratelimiterRedis.limiter(req, options)
357
+ const x = await ratelimiterRedis.limiter(req, options)
357
358
  console.log(304, x)
358
359
  }
359
360
  catch(e) {
360
361
  expect(e).to.be.an('error')
361
362
  expect(e).to.have.property('message', 'tooManyRequestsFromThisIP')
362
- expect(e).to.have.property('status', 429)
363
+ expect(e).to.have.property('code', 429)
363
364
  }
364
365
  })
365
366
 
@@ -376,7 +377,7 @@ describe('Use Redis', () => {
376
377
  })
377
378
 
378
379
  it('req #1 should not trigger', async() => {
379
- let result = await ratelimiterRedis.limiter(req, options)
380
+ const result = await ratelimiterRedis.limiter(req, options)
380
381
  expect(result).eql(undefined)
381
382
  })
382
383
 
@@ -388,13 +389,13 @@ describe('Use Redis', () => {
388
389
  catch(e) {
389
390
  expect(e).to.be.an('error')
390
391
  expect(e).to.have.property('message', 'finalThrottlingActive_requestsIsDelayed')
391
- expect(e).to.have.property('status', 900)
392
+ expect(e).to.have.property('code', 900)
392
393
  expect(e.additionalInfo).to.have.property('counter', 2)
393
394
  }
394
395
  })
395
396
 
396
397
  it('req #3 should not trigger the limiter - the throttling has reset the limiter', async() => {
397
- let result = await ratelimiterRedis.limiter(req, options)
398
+ const result = await ratelimiterRedis.limiter(req, options)
398
399
  expect(result).eql(undefined)
399
400
  })
400
401
 
package/.eslintrc.js DELETED
@@ -1,28 +0,0 @@
1
- const config = {
2
- root: true,
3
- 'env': {
4
- 'commonjs': true,
5
- 'es6': true,
6
- 'node': true
7
- },
8
- 'extends': 'eslint:recommended',
9
- "rules": {
10
- "space-before-function-paren": 0,
11
- "no-extra-semi": 0,
12
- "object-curly-spacing": ["error", "always"],
13
- "brace-style": ["error", "stroustrup", { "allowSingleLine": true }],
14
- "no-useless-escape": 0,
15
- "standard/no-callback-literal": 0,
16
- "new-cap": 0
17
- },
18
- globals: {
19
- describe: true,
20
- it: true,
21
- after: true
22
- },
23
- 'parserOptions': {
24
- 'ecmaVersion': 2022
25
- },
26
- }
27
-
28
- module.exports = config