/*jshint laxcomma: true, smarttabs: true, node: true, esnext: true */
'use strict';
/**
* A No op resource throttle implementation, for testing / debuging
* @module tastypie/lib/throttle
* @author Eric Satterwhite
* @since 0.2.2
* @requires tastypie/lib/class
* @requires tastypie/lib/class/options
* @requires mout/string/slugify
* @requires tastypie/lib/class/parent
*/
var Class = require( './class' )
, Options = require( './class/options' )
, Parent = require( './class/parent' )
, slugify = require('mout/string/slugify')
, debug = require('debug')('tastypie:throttle')
, Throttle
, Memory
;
/**
* @constructor
* @alias module:tastypie/lib/throttle
* @param {Object} [options]
* @param {Number} [options.at=150] number of request to start throttling
*/
Throttle = new Class({
mixin:[ Options, Parent ]
,options:{
at:150
}
,constructor: function( options ){
this.setOptions( options );
}
/**
* Records an entry for a given keyf
* @chainable
* @method module:tastypie/lib/throttle#incr
* @param {String} id the internal key to record
**/
,incr: function incr(){
return this;
}
/**
* Checks if a given key has reached the configured throttling limit
* @method module:tastypie/lib/throttle#toThrottle
* @param {String} id the internal key to record
* @returns {Boolean}
**/
,toThrottle: function toThrottle(){
return false;
}
/**
* creates a namespaced key based on an id
* @method module:tastypie/lib/throttle#convert
* @param {String} id the internal key to record
* @return {String} key
**/
,convert: function convert( id ){
return `access-${slugify( id )}`;
}
});
/**
* An in memory throttle implementation. For testing and debugging purposes only
* @class module:tastypie/lib/throttle.Memory
* @param {Object} [options]
* @param {Number} [options.at=150] The number of requests allowed during a timeframe before they should be throttled
* @param {Number} [options.timeframe=30000] The time frame in miliseconds to determine number of attempts before throttling
* @param {?Number} [options.expires=null] time in ms after which to expire throttle records
*/
Memory = new Class({
inherits:Throttle
,options:{
at: 150,
timeframe: ( 1000 * 60 * 5),
expires: null
}
,constructor: function( options ){
this.setOptions( options );
this._mem = Object.create(null)
const expires = this.options.expires;
expires && setInterval(() => {
for( var key in this._mem ) {
const now = new Date();
const first = this._mem[key][0];
const diff = first ? now - first : 0;
if( diff > expires ){
debug("purging record for %s ", key );
this._mem[key].shift();
}
}
}, expires).unref();
}
/**
* Records an entry for a given keyf
* @chainable
* @method module:tastypie/lib/throttle.Memory#incr
* @param {String} id the internal key to record
**/
,incr: function incr( id ){
var key = this.convert( id );
( this._mem[ key ] = this._mem[ key ] || [] ).push( new Date() );
return this;
}
/**
* Checks if a given key has reached the configured throttling limit
* @method module:tastypie/lib/throttle.Memory#toThrottle
* @param {String} id the internal key to record
* @returns {Boolean}
**/
,toThrottle: function toThrottle( id ){
var that // reference to this
, key // lookup key
, now // current date
, attempts // number of attempts for lookup key
;
that = this;
key = this.convert( id );
now = new Date();
attempts = ( this._mem [ key ] || [])
.filter( function( time ){
return ( now - time ) < that.options.timeframe;
});
return attempts.length >= this.options.at;
}
/**
* Returns internal memory
* @private
* @method module:tastypie/lib/throttle.Memory#toJSON
* @returns {Object}
**/
,toJSON: function toJSON(){
return this._mem;
}
/**
* creates a namespaced key based on an id
* @method module:tastypie/lib/throttle.Memory#convert
* @param {String} id the internal key to record
* @return {String} key
**/
,convert: function convert( id ){
return `access-${slugify( id )}`;
}
});
module.exports = Throttle;
module.exports.Memory = Memory;