Skip to content

Commit

Permalink
feat: LimitingInterceptor can now be configured to delay or reject re…
Browse files Browse the repository at this point in the history
…quests
  • Loading branch information
arlac77 committed Jan 12, 2016
1 parent 744fee9 commit f5404a1
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 20 deletions.
57 changes: 43 additions & 14 deletions lib/LimitingInterceptor.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@
const Interceptor = require('./interceptor').Interceptor;

/**
* limits the number of concurrent requests
* limits the number of concurrent requests.
* Requests can be delayed or rejected.
* [
* { requests: 5, delay: 10 },
* { requests: 10, delay: 100 },
* { requests: 20, delay: 1000 },
* { request: 50: reject: true }
* { count: 20 },
* { count: 10, delay: 100 },
* { count: 5, delay: 10 }
* ]
* 1 - 4 - no delay
* 5 - 9 - 10ms delay
* 10 - 20 100ms delay
* 1 - 4 : no delay
* 5 - 9 : 10ms delay
* 10 - 19 : 100ms delay
* 20 : reject
*/
class LimitingInterceptor extends Interceptor {
static get type() {
Expand All @@ -28,29 +29,57 @@ class LimitingInterceptor extends Interceptor {
constructor(endpoint, config) {
super(endpoint, config);

Object.defineProperty(this, 'limit', {
value: config ? config.limit : 10
Object.defineProperty(this, 'limits', {
value: config ? config.limits : [{
count: 10
}]
});

this.ongoingResponses = new Set();
this.ongoingRequests = 0;
}

reset() {
this.ongoingResponses = new Set();
this.ongoingRequests = 0;
}

receive(request, oldRequest) {
if (this.ongoingResponses.size >= this.limit) {
return Promise.reject(new Error(`Limit of ongoing requests ${this.limit} reached`));
//console.log(`got #${this.ongoingRequests}`);

for (let limit of this.limits) {
if (this.ongoingRequests >= limit.count) {
if (limit.delay === undefined) {
//console.log(`-> reject`);
return Promise.reject(new Error(`Limit of ongoing requests ${limit.count} reached`));
}

//console.log(`-> delay ${limit.delay}`);
this.ongoingRequests += 1;

return new Promise((fullfill, reject) => {
setTimeout(() => {
//console.log(`${limit.delay} done -> go on`);
fullfill(this._processRequest(request, oldRequest));
}, limit.delay);
});
}
}

let response = this.connected.receive(request, oldRequest);
//console.log(`-> normal`);
this.ongoingRequests += 1;

return this._processRequest(request, oldRequest);
}

const currentResponse = response.then(resolved => {
_processRequest(request, oldRequest) {
const currentResponse = this.connected.receive(request, oldRequest).then(resolved => {
this.ongoingResponses.delete(currentResponse);
this.ongoingRequests -= 1;
return resolved;
}).catch(rejected => {
this.ongoingResponses.delete(currentResponse);
this.ongoingRequests -= 1;
return rejected;
});

Expand Down
5 changes: 5 additions & 0 deletions lib/interceptor.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ class Interceptor extends connectorMixin(_DummyInterceptor) {
return `${this.endpoint}[${this.type}]`;
}

/**
* forget all accumulated information
*/
reset() {}

/**
* The receive method. This method receives the request from the leading interceptor and calls the
* trailling interceptor
Expand Down
17 changes: 11 additions & 6 deletions tests/interceptor_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,32 +53,37 @@ describe('interceptors', () => {
mochaInterceptorTest(TimeLoggerInterceptor, ep, {}, "logger-request-time", itc => {});


const REQUEST_LIMIT = 5;
const REQUEST_LIMIT = 2;

mochaInterceptorTest(LimitingInterceptor, ep, {
limit: REQUEST_LIMIT
limits: [{
count: REQUEST_LIMIT * 2,
}, {
count: REQUEST_LIMIT,
delay: 10
}]
}, "request-limit", (itc, withConfig) => {
it('has limit', () => assert.equal(itc.limit, REQUEST_LIMIT));
it('has limits', () => assert.equal(itc.limits[0].count, REQUEST_LIMIT * 2));

itc.connected = dummyEndpoint('ep');

// request value is the timeout
itc.connected.receive = request =>
new Promise((fullfilled, rejected) =>
setTimeout(() => fullfilled(request), 200));
setTimeout(() => fullfilled(request), 1000));

if (withConfig) {
it('sending lots of request', done => {
let i;
for (i = 0; i < REQUEST_LIMIT + 1; i++) {
for (i = 0; i < (REQUEST_LIMIT * 2) + 1; i++) {
const response = itc.receive(i).then(
f => {
console.log(`fullfilled: ${f}`);
},
r => {
console.log(`rejected: ${r}`);

if (i >= REQUEST_LIMIT) {
if (i >= REQUEST_LIMIT * 2) {
done();
}
}
Expand Down

0 comments on commit f5404a1

Please sign in to comment.