From 84c909a5dbf1b2613dfff5a40854de0698d44d0f Mon Sep 17 00:00:00 2001 From: John Crepezzi Date: Sun, 6 Mar 2016 16:20:40 -0500 Subject: [PATCH 1/2] Added user-configurable rate limiting --- config.js | 9 +++++ package.json | 5 ++- server.js | 96 ++++++++++++++++++++++++++++++++-------------------- 3 files changed, 72 insertions(+), 38 deletions(-) diff --git a/config.js b/config.js index 218ae16..52ad004 100644 --- a/config.js +++ b/config.js @@ -23,6 +23,15 @@ "type": "phonetic" }, + "rateLimits": { + "categories": { + "normal": { + "totalRequests": 500, + "every": 60000 + } + } + }, + "storage": { "type": "redis", "host": "0.0.0.0", diff --git a/package.json b/package.json index 6bf128b..5d5bcc2 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,11 @@ }, "main": "haste", "dependencies": { + "connect-ratelimit": "0.0.7", + "connect-route": "0.1.5", + "connect": "3.4.1", + "st": "1.1.0", "winston": "0.6.2", - "connect": "1.9.2", "redis-url": "0.1.0", "redis": "0.8.1", "uglify-js": "1.3.3", diff --git a/server.js b/server.js index 9daa6e7..2e138f2 100644 --- a/server.js +++ b/server.js @@ -4,6 +4,9 @@ var fs = require('fs'); var winston = require('winston'); var connect = require('connect'); +var route = require('connect-route'); +var connect_st = require('st'); +var connect_rate_limit = require('connect-ratelimit'); var DocumentHandler = require('./lib/document_handler'); @@ -99,42 +102,61 @@ var documentHandler = new DocumentHandler({ keyGenerator: keyGenerator }); -// Set the server up with a static cache -connect.createServer( - // First look for api calls - connect.router(function(app) { - // get raw documents - support getting with extension - app.get('/raw/:id', function(request, response, next) { - var skipExpire = !!config.documents[request.params.id]; - var key = request.params.id.split('.')[0]; - return documentHandler.handleRawGet(key, response, skipExpire); - }); - // add documents - app.post('/documents', function(request, response, next) { - return documentHandler.handlePost(request, response); - }); - // get documents - app.get('/documents/:id', function(request, response, next) { - var skipExpire = !!config.documents[request.params.id]; - return documentHandler.handleGet( - request.params.id, - response, - skipExpire - ); - }); - }), - // Otherwise, static - connect.staticCache(), - connect.static(__dirname + '/static', { maxAge: config.staticMaxAge }), - // Then we can loop back - and everything else should be a token, - // so route it back to /index.html - connect.router(function(app) { - app.get('/:id', function(request, response, next) { - request.url = request.originalUrl = '/index.html'; - next(); - }); - }), - connect.static(__dirname + '/static', { maxAge: config.staticMaxAge }) -).listen(config.port, config.host); +var app = connect(); + +// Rate limit all requests +if (config.rateLimits) { + config.rateLimits.end = true; + app.use(connect_rate_limit(config.rateLimits)); +} + +// first look at API calls +app.use(route(function(router) { + // get raw documents - support getting with extension + router.get('/raw/:id', function(request, response, next) { + var skipExpire = !!config.documents[request.params.id]; + var key = request.params.id.split('.')[0]; + return documentHandler.handleRawGet(key, response, skipExpire); + }); + // add documents + router.post('/documents', function(request, response, next) { + return documentHandler.handlePost(request, response); + }); + // get documents + router.get('/documents/:id', function(request, response, next) { + var skipExpire = !!config.documents[request.params.id]; + return documentHandler.handleGet( + request.params.id, + response, + skipExpire + ); + }); +})); + +// Otherwise, try to match static files +app.use(connect_st({ + path: __dirname + '/static', + content: { maxAge: config.staticMaxAge }, + passthrough: true, + index: false +})); + +// Then we can loop back - and everything else should be a token, +// so route it back to / +app.use(route(function(router) { + router.get('/:id', function(request, response, next) { + request.sturl = '/'; + next(); + }); +})); + +// And match index +app.use(connect_st({ + path: __dirname + '/static', + content: { maxAge: config.staticMaxAge }, + index: 'index.html' +})); + +http.createServer(app).listen(config.port, config.host); winston.info('listening on ' + config.host + ':' + config.port); From fbb6e63c37118a1b7c14806dd7520beb234586b7 Mon Sep 17 00:00:00 2001 From: John Crepezzi Date: Sun, 6 Mar 2016 16:34:33 -0500 Subject: [PATCH 2/2] Added a note to the README --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index e3acf6c..c53373a 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,16 @@ STDOUT. Check the README there for more details and usages. * `storage` - storage options (see below) * `logging` - logging preferences * `keyGenerator` - key generator options (see below) +* `rateLimits` - settings for rate limiting (see below) + +## Rate Limiting + +When present, the `rateLimits` option enables built-in rate limiting courtesy +of `connect-ratelimit`. Any of the options supported by that library can be +used and set in `config.json`. + +See the README for [connect-ratelimit](https://github.com/dharmafly/connect-ratelimit) +for more information! ## Key Generation