Skip to content

Commit 83f4b94

Browse files
authored
Merge pull request #58 from pierotofy/flood
Added flood monitor
2 parents ba612ab + c459761 commit 83f4b94

File tree

5 files changed

+89
-1
lines changed

5 files changed

+89
-1
lines changed

config-default.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"debug": false,
1313
"log-level": "info",
1414
"upload-max-speed": 0,
15+
"flood-limit": 0,
1516
"ssl-key": "",
1617
"ssl-cert": "",
1718
"asr-provider": "",

config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ let argDefs = {
4545
default: defaultConfig,
4646

4747
int: ['port', 'admin-cli-port', 'admin-web-port',
48-
'secure-port', 'upload-max-speed'] // for cast only, not used by minimist
48+
'secure-port', 'upload-max-speed', 'flood-limit'] // for cast only, not used by minimist
4949
};
5050
let argv = require('minimist')(process.argv.slice(2), argDefs);
5151

@@ -66,6 +66,7 @@ Options:
6666
--downloads-from-s3 <URL> Manually set the S3 URL prefix where to redirect /task/<uuid>/download requests. (default: do not use S3, forward download requests to nodes, unless the autoscaler is setup, in which case the autoscaler's S3 configuration is used)
6767
--no-splitmerge By default the program will set itself as being a cluster node for all split/merge tasks. Setting this option disables it. (default: false)
6868
--public-address <http(s)://host:port> Should be set to a public URL that nodes can use to reach ClusterODM. (default: match the "host" header from client's HTTP request)
69+
--flood-limit <number> Limit the number of simultaneous task uploads that a user can initiate concurrently (default: no limit)
6970
--token <token> Sets a token that needs to be passed for every request. This can be used to limit access to the node only to token holders. (default: none)
7071
--debug Disable caches and other settings to facilitate debug (default: false)
7172
--ssl-key <file> Path to .pem SSL key file

index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const logger = require('./libs/logger');
2222
const package_info = require('./package_info');
2323
const nodes = require('./libs/nodes');
2424
const proxy = require('./libs/proxy');
25+
const floodMonitor = require('./libs/floodMonitor');
2526
const routetable = require('./libs/routetable');
2627

2728
(async function(){
@@ -33,6 +34,7 @@ const routetable = require('./libs/routetable');
3334
const cloudProvider = (require('./libs/cloudProvider')).initialize(config.cloud_provider);
3435
await (require('./libs/asrProvider')).initialize(config.asr);
3536
await nodes.initialize();
37+
floodMonitor.initialize();
3638

3739
const proxies = await proxy.initialize(cloudProvider);
3840

libs/floodMonitor.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* ClusterODM - A reverse proxy, load balancer and task tracker for NodeODM
3+
* Copyright (C) 2018-present MasseranoLabs LLC
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as
7+
* published by the Free Software Foundation, either version 3 of the
8+
* License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
const config = require('../config');
19+
20+
let userTasks = null;
21+
22+
module.exports = {
23+
initialize: function(){
24+
userTasks = {};
25+
26+
const forgive = () => {
27+
Object.keys(userTasks).forEach(userToken => {
28+
if (userTasks[userToken].count > 0){
29+
userTasks[userToken].count = Math.floor(userTasks[userToken].count * 0.66);
30+
}
31+
32+
if (userTasks[userToken].count <= 0){
33+
delete(userTasks[userToken]);
34+
}
35+
});
36+
}
37+
38+
setInterval(forgive, 1000 * 60 * this.FORGIVE_TIME);
39+
},
40+
41+
FORGIVE_TIME: 15, // minutes
42+
43+
recordTaskInit: function(userToken){
44+
this.modifyRecord(userToken, record => {
45+
record.count = record.count ? (record.count + 1) : 1;
46+
});
47+
},
48+
49+
recordTaskCommit: function(userToken){
50+
this.modifyRecord(userToken, record => {
51+
record.count = Math.max(record.count - 1, 0);
52+
});
53+
},
54+
55+
isFlooding: function(userToken){
56+
if (config.flood_limit <= 0) return false; // Disabled
57+
if (!userToken) userToken = "default";
58+
59+
const record = userTasks[userToken];
60+
if (!record) return false; // No record
61+
62+
return record.count > config.flood_limit;
63+
},
64+
65+
modifyRecord: function(userToken, callback){
66+
if (!userToken) userToken = "default";
67+
68+
const record = userTasks[userToken] || {};
69+
70+
callback(record);
71+
72+
userTasks[userToken] = record;
73+
}
74+
};

libs/proxy.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const taskNew = require('./taskNew');
3535
const async = require('async');
3636
const odmOptions = require('./odmOptions');
3737
const asrProvider = require('./asrProvider');
38+
const floodMonitor = require('./floodMonitor');
3839

3940
module.exports = {
4041
initialize: async function(cloudProvider){
@@ -259,6 +260,13 @@ module.exports = {
259260
return;
260261
}
261262

263+
floodMonitor.recordTaskInit(query.token);
264+
265+
if (floodMonitor.isFlooding(query.token)){
266+
die(`Uuh, slow down! It seems like you are sending a lot of tasks. Check that your connection is not dropping, or wait ${floodMonitor.FORGIVE_TIME} minutes and try again.`);
267+
return;
268+
}
269+
262270
// Save
263271
fs.writeFile(path.join(tmpPath, "body.json"),
264272
JSON.stringify(params), {encoding: 'utf8'}, err => {
@@ -307,6 +315,8 @@ module.exports = {
307315
return;
308316
}
309317

318+
floodMonitor.recordTaskCommit(query.token);
319+
310320
async.series([
311321
cb => {
312322
fs.readFile(bodyFile, 'utf8', (err, data) => {

0 commit comments

Comments
 (0)