All files / app/rate_limit middleware.js

81.81% Statements 18/22
91.66% Branches 11/12
100% Functions 2/2
81.81% Lines 18/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58          2x 2x             10x 10x 10x         8x 1x     7x 7x           7x 7x   7x 7x   7x 1x 1x           6x               2x        
// Part II & III - Express middleware that rate limits by client IP.
//
// Applied to the sensitive zip-generation endpoint. It resolves the caller's
// IP, spends the configured token cost from that IP's bucket, and either lets
// the request through or answers 429 Too Many Requests.
const config = require('../config');
const { consume } = require('./rate_limiter');
 
// Resolve the client IP the way the TP suggests:
//   var ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress || null;
// `x-forwarded-for` can be a comma-separated list "client, proxy1, proxy2"; the
// original client is the first entry.
function clientIp(req) {
  const forwarded = req.headers['x-forwarded-for'];
  const ip = forwarded || (req.socket && req.socket.remoteAddress) || null;
  return ip ? String(ip).split(',')[0].trim() : null;
}
 
// Express middleware. No-op when rate limiting is disabled (e.g. tests).
async function rateLimit(req, res, next) {
  if (!config.rateLimitEnabled) {
    return next();
  }
 
  const ip = clientIp(req);
  Iif (!ip) {
    // Can't identify the caller: fail open rather than block everyone.
    console.warn('[ratelimit] could not determine client IP; letting request through');
    return next();
  }
 
  try {
    const { allowed, remaining, retryAfter, limit } = await consume(ip);
 
    res.setHeader('X-RateLimit-Limit', limit);
    res.setHeader('X-RateLimit-Remaining', remaining);
 
    if (!allowed) {
      res.setHeader('Retry-After', retryAfter);
      return res.status(429).send({
        error: 'Too many requests. Please slow down and try again shortly.',
        retryAfter
      });
    }
 
    return next();
  } catch (err) {
    // Never let a limiter failure take down the endpoint: fail open and log.
    console.error('[ratelimit] unexpected error, letting request through:', err);
    return next();
  }
}
 
module.exports = {
  rateLimit,
  clientIp
};