diff --git a/lib/configBase.js b/lib/configBase.js index 33fd0e0..a04a266 100644 --- a/lib/configBase.js +++ b/lib/configBase.js @@ -106,6 +106,12 @@ exports.longFailingTimeout = 10000; */ exports.minQuorum = 2; +/* + Max retry time (milliseconds) + the sum of the different retry intervals must be less than this value + */ +exports.maxRetryTime = 20 * 60 * 1000; //20 mins + /* LISTENER AND CONSUMER CONFIGURATION ====================================== diff --git a/lib/eventWorker.js b/lib/eventWorker.js index 64941cc..6c6840a 100644 --- a/lib/eventWorker.js +++ b/lib/eventWorker.js @@ -165,6 +165,20 @@ function validateHeaders(simpleRequest) { errorsHeaders.push(error); } + + var sum = 0; + for (var i = 0; i < retrySplit.length; i++) { + sum += Number(retrySplit[i]); + } + + if (sum > configGlobal.maxRetryTime) { + var error = {} + error.type = MG.INVALID_PARAMETER; + error.parameter = MG.HEAD_RELAYER_RETRY; + error.userMessage = 'The sum of the different intervals must be less than ' + configGlobal.maxRetryTime +' ms'; + + errorsHeaders.push(error); + } } //check Persistence Header diff --git a/test/acceptance/config.js b/test/acceptance/config.js index 6155e0e..455fc6a 100644 --- a/test/acceptance/config.js +++ b/test/acceptance/config.js @@ -1,4 +1,3 @@ -exports.rushServer = {hostname: '54.229.149.119', port: 443}; -exports.externalEndpoint = 'www.google.com'; -exports.user = 'rush:RuSh1'; +exports.rushServer = {hostname: 'ec2-54-229-212-149.eu-west-1.compute.amazonaws.com', port: 443}; +exports.externalEndpoint = 'www.google.es'; exports.token = ''; diff --git a/test/e2e/maxRetryTest.js b/test/e2e/maxRetryTest.js new file mode 100644 index 0000000..9970f55 --- /dev/null +++ b/test/e2e/maxRetryTest.js @@ -0,0 +1,182 @@ +var should = require('should'); +var superagent = require('superagent'); +var config = require('./config'); +var globalConfig = require('../../lib/configBase.js'); +var _ = require('underscore'); +var chai = require('chai'); +var expect = chai.expect; +var redisModule = require('redis'); + +var listener = require('../../lib/listener.js'); + +//RUSH ENDPOINT +var HOST = config.rushServer.hostname; +var PORT = config.rushServer.port; +var RUSHENDPOINT = 'http://' + HOST + ':' + PORT; + +//Final host endpoint +var fhHOST = config.simpleServerHostname; +var fhPORT = config.simpleServerPort; + +ENDPOINT = fhHOST + ':' + fhPORT; + +// Time to wait to check the status of the task +var TIMEOUT = 600; +var CREATED = 201; +var describeTimeout = 5000; + +function _validScenario(data){ + + var values = []; + var counter = 0, value; + + while((counter + (value = _.random(0, globalConfig.maxRetryTime))) <= globalConfig.maxRetryTime) { + counter += value; + values.push(value) + } + + values.push(globalConfig.maxRetryTime - counter); + + var times = values.toString(); + + + it(data.name +' /' +data.method +' #FRT', function(done){ + var agent = superagent.agent(); + var id; + + var method; + switch(data.method){ + case "DELETE": + method = 'del'; + break; + default: + method = data.method.toLowerCase() + } + var req = agent + [method](RUSHENDPOINT + data.path ) + .set('x-relayer-host', ENDPOINT) //Always the same endpoint + .set('x-relayer-persistence','BODY') + .set('content-type','application/json') + .set('x-relayer-retry', times) + .set(data.headers) + if(data.method === 'POST' || data.method === 'PUT'){ + req = req.send(data.body); + } + req.end(function(err, res) { + expect(err).to.not.exist; + expect(res.statusCode).to.eql(CREATED); + expect(res.body).to.exist; + expect(res.body.id).to.exist; + id=res.body.id; + res.text.should.not.include('exception'); + done(); + }); + }); +} + +function _invalidScenario(data){ + + var values = []; + var counter = 0, value; + var maxRetry = globalConfig.maxRetryTime; + + while((counter + (value = _.random(0, maxRetry))) <= maxRetry) { + counter += value; + values.push(value) + } + + values.push(maxRetry - counter + _.random(0, maxRetry)); + + var times = values.toString(); + + //it(data.name, function(done){ + //it(data.name + data.protocol.toUpperCase() + ' /' +data.method + ' #FRT', function(done){ + it(data.name + ' /' +data.method + ' #FRT', function(done){ + var agent = superagent.agent(); + var id; + + var method; + switch(data.method){ + case "DELETE": + method = 'del'; + break; + default: + method = data.method.toLowerCase() + } + var req = agent + [method](RUSHENDPOINT + data.path ) + .set('x-relayer-host', ENDPOINT) //Always the same endpoint + .set('x-relayer-persistence','BODY') + .set('content-type','application/json') + .set('x-relayer-retry', times) + .set(data.headers) + if(data.method === 'POST' || data.method === 'PUT'){ + req = req.send(data.body); + } + req.end(function(err, res) { + expect(err).to.not.exist; + expect(res.statusCode).to.eql(400); + expect(res.body).to.exist; + expect(res.body.exceptionId).to.eql('SVC0002'); + expect(res.body.exceptionText).to.eql('Invalid parameter value: x-relayer-retry'); + expect(res.body.userMessage).to.eql('The sum of the different intervals must be less than ' + maxRetry + ' ms'); + done(); + }); + }); +} + +describe('Multiple Feature: Maximum Retry value', function() { + this.timeout(TIMEOUT); + + before(function (done) { + listener.start(done); + }); + + after(function (done) { + var rc = redisModule.createClient(globalConfig.queue.redisPort, globalConfig.queue.redisHost); + rc.flushall(function(){ + listener.stop(done); + }); + rc.quit(); + }); + + describe('Retrieve requests with a valid RETRY headers policy ', function () { + + var dataSet = [ + {method: 'GET', path: '/', headers: {'X-Relayer-Protocol':'http'}, body: {}, name : "Case 1 Should accept the request using HTTP "}, + {method: 'POST', path: '/', headers: {'X-Relayer-Protocol':'http'}, body: {}, name : "Case 2 Should accept the request using HTTP "}, + {method: 'PUT', path: '/', headers: {'X-Relayer-Protocol':'http'}, body: {}, name : "Case 3 Should accept the request using HTTP "}, + {method: 'DELETE', path: '/', headers: {'X-Relayer-Protocol':'http'}, body: {}, name : "Case 4 Should accept the request using HTTP "}, + {method: 'GET', path: '/', headers: {'X-Relayer-Protocol':'https'}, body: {}, name : "Case 5 Should accept the request using HTTPS "}, + {method: 'POST', path: '/', headers: {'X-Relayer-Protocol':'https'}, body: {}, name : "Case 6 Should accept the request using HTTPS "}, + {method: 'PUT', path: '/', headers: {'X-Relayer-Protocol':'https'}, body: {}, name : "Case 7 Should accept the request using HTTPS "}, + {method: 'DELETE', path: '/', headers: {'X-Relayer-Protocol':'https'}, body: {}, name : "Case 8 Should accept the request using HTTPS "} + ]; + + for(i=0; i < dataSet.length; i++){ + _validScenario(dataSet[i]); //Launch every test in data set + } + }); + + describe('Retrieve requests with a valid RETRY headers policy over the MAXIMUM set', function () { + + var dataSetPOST = [ + {method: 'GET', path: '/', headers: {'X-Relayer-Protocol':'http'}, body: {}, name : "Case 1 Should NOT accept the request using HTTP "}, + {method: 'POST', path: '/', headers: {'X-Relayer-Protocol':'http'}, body: {}, name : "Case 2 Should NOT accept the request using HTTP "}, + {method: 'PUT', path: '/', headers: {'X-Relayer-Protocol':'http'}, body: {}, name : "Case 3 Should NOT accept the request using HTTP "}, + {method: 'DELETE', path: '/', headers: {'X-Relayer-Protocol':'http'}, body: {}, name : "Case 4 Should NOT accept the request using HTTP "}, + {method: 'GET', path: '/', headers: {'X-Relayer-Protocol':'https'}, body: {}, name : "Case 5 Should NOT accept the request using HTTPS "}, + {method: 'POST', path: '/', headers: {'X-Relayer-Protocol':'https'}, body: {}, name : "Case 6 Should NOT accept the request using HTTPS "}, + {method: 'PUT', path: '/', headers: {'X-Relayer-Protocol':'https'}, body: {}, name : "Case 7 Should NOT accept the request using HTTPS "}, + {method: 'DELETE', path: '/', headers: {'X-Relayer-Protocol':'https'}, body: {}, name : "Case 8 Should NOT accept the request using HTTPS "} + ]; + + for(i=0; i < dataSetPOST.length; i++){ + _invalidScenario(dataSetPOST[i]); //Launch every test in data set + } + }); + + +}); + +//TODO: path different to empty