Skip to content

Commit

Permalink
Merge pull request #244 from takeshirs/feature/custom-req-param-name
Browse files Browse the repository at this point in the history
add option to allow customizing param name added to req (default is still req.rateLimit)
  • Loading branch information
nfriedly committed Oct 1, 2021
2 parents 87e4c83 + 23508f7 commit 6912114
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 4 deletions.
9 changes: 8 additions & 1 deletion README.md
Expand Up @@ -102,6 +102,8 @@ app.post("/create-account", createAccountLimiter, function(req, res) {

A `req.rateLimit` property is added to all requests with the `limit`, `current`, and `remaining` number of requests and, if the store provides it, a `resetTime` Date object. These may be used in your application code to take additional actions or inform the user of their status.

The property name can be configured with the configuration option `requestPropertyName`

## Configuration options

### max
Expand Down Expand Up @@ -255,6 +257,11 @@ function (/*req, res*/) {
}
```

### requestPropertyName
Parameter to add to `req`-Object.

Defaults to `rateLimit`.

### store

The storage to use when persisting rate limit attempts.
Expand Down Expand Up @@ -339,4 +346,4 @@ v2 uses a less precise but less resource intensive method of tracking hits from

## License

MIT © [Nathan Friedly](http://nfriedly.com/)
MIT © [Nathan Friedly](http://nfriedly.com/)
13 changes: 10 additions & 3 deletions lib/express-rate-limit.js
Expand Up @@ -27,6 +27,7 @@ function RateLimit(options) {
res.status(options.statusCode).send(options.message);
},
onLimitReached: function (/*req, res, optionsUsed*/) {},
requestPropertyName: "rateLimit", // Parameter name appended to req object
},
options
);
Expand Down Expand Up @@ -74,7 +75,7 @@ function RateLimit(options) {

Promise.resolve(maxResult)
.then((max) => {
req.rateLimit = {
req[options.requestPropertyName] = {
limit: max,
current: current,
remaining: Math.max(max - current, 0),
Expand All @@ -83,7 +84,10 @@ function RateLimit(options) {

if (options.headers && !res.headersSent) {
res.setHeader("X-RateLimit-Limit", max);
res.setHeader("X-RateLimit-Remaining", req.rateLimit.remaining);
res.setHeader(
"X-RateLimit-Remaining",
req[options.requestPropertyName].remaining
);
if (resetTime instanceof Date) {
// if we have a resetTime, also provide the current date to help avoid issues with incorrect clocks
res.setHeader("Date", new Date().toUTCString());
Expand All @@ -95,7 +99,10 @@ function RateLimit(options) {
}
if (options.draft_polli_ratelimit_headers && !res.headersSent) {
res.setHeader("RateLimit-Limit", max);
res.setHeader("RateLimit-Remaining", req.rateLimit.remaining);
res.setHeader(
"RateLimit-Remaining",
req[options.requestPropertyName].remaining
);
if (resetTime) {
const deltaSeconds = Math.ceil(
(resetTime.getTime() - Date.now()) / 1000
Expand Down
45 changes: 45 additions & 0 deletions test/express-rate-limit-test.js
Expand Up @@ -641,4 +641,49 @@ describe("express-rate-limit node module", () => {
await request(app).get("/").expect(429);
assert(errorCaught, "error should have been caught");
});

it("should handle two rate-limiters independtly", async () => {
const limiter = rateLimit({
max: 2,
keyGenerator: function (req, res) {
assert.ok(req);
assert.ok(res);

const key = req.query.key;
assert.ok(key);

return key;
},
requestPropertyName: "rateLimitIp",
handler: function (req, res) {
res.status(429).end("keyLimiter handler executed!");
},
});

const globalLimiter = rateLimit({
max: 5,
keyGenerator: () => {
"global";
},
requestPropertyName: "rateLimitGlobal",
handler: function (req, res) {
res.status(429).end("globalLimiter handler executed!");
},
});

createAppWith([limiter, globalLimiter]);
await request(app).get("/").query({ key: 1 }).expect(200); // keyLimiter[1]: 1, keyLimiter[2]: 0, keyLimiter[3]: 0, global: 1
await request(app).get("/").query({ key: 2 }).expect(200); // keyLimiter[1]: 1, keyLimiter[2]: 1, keyLimiter[3]: 0, global: 2
await request(app).get("/").query({ key: 1 }).expect(200); // keyLimiter[1]: 2, keyLimiter[2]: 1, keyLimiter[3]: 0, global: 3
await request(app).get("/").query({ key: 2 }).expect(200); // keyLimiter[1]: 2, keyLimiter[2]: 2, keyLimiter[3]: 0, global: 4
await request(app)
.get("/")
.query({ key: 1 })
.expect(429, "keyLimiter handler executed!"); // keyLimiter[1]: 3 > 2!
await request(app).get("/").query({ key: 3 }).expect(200); // keyLimiter[1]: 2, keyLimiter[2]: 2, keyLimiter[3]: 1, global: 5
await request(app)
.get("/")
.query({ key: 3 })
.expect(429, "globalLimiter handler executed!"); // keyLimiter[1]: 2, keyLimiter[2]: 2, keyLimiter[3]: 2, global: 6 > 5!
});
});

0 comments on commit 6912114

Please sign in to comment.