xhr-shaper
Advanced tools
Comparing version 0.2.0 to 0.2.1
364
index.js
@@ -6,31 +6,31 @@ 'use strict'; | ||
var XHRShaper = function() { | ||
var _maxBandwidth = Infinity; | ||
var _minLatency = 0; | ||
var _minProgressEvents = 0; | ||
var _randomness = 0; | ||
var _maxBandwidth = Infinity; | ||
var _minLatency = 0; | ||
var _minProgressEvents = 0; | ||
var _randomness = 0; | ||
Object.defineProperty(this, "maxBandwidth", { | ||
get: function() { | ||
return Math.min(_maxBandwidth, XHRShaper.maxBandwidth); | ||
}, | ||
set: function(val) { _maxBandwidth = val } | ||
}); | ||
Object.defineProperty(this, "minLatency", { | ||
get: function() { | ||
return Math.max(_minLatency, XHRShaper.minLatency); | ||
}, | ||
set: function(val) { _minLatency = val } | ||
}); | ||
Object.defineProperty(this, "minProgressEvents", { | ||
get: function() { | ||
return Math.max(_minProgressEvents, XHRShaper.minProgressEvents); | ||
}, | ||
set: function(val) { _minProgressEvents = val } | ||
}); | ||
Object.defineProperty(this, "randomness", { | ||
get: function() { | ||
return Math.max(_randomness, XHRShaper.randomness); | ||
}, | ||
set: function(val) { _randomness = val } | ||
}); | ||
Object.defineProperty(this, "maxBandwidth", { | ||
get: function() { | ||
return Math.min(_maxBandwidth, XHRShaper.maxBandwidth); | ||
}, | ||
set: function(val) { _maxBandwidth = val } | ||
}); | ||
Object.defineProperty(this, "minLatency", { | ||
get: function() { | ||
return Math.max(_minLatency, XHRShaper.minLatency); | ||
}, | ||
set: function(val) { _minLatency = val } | ||
}); | ||
Object.defineProperty(this, "minProgressEvents", { | ||
get: function() { | ||
return Math.max(_minProgressEvents, XHRShaper.minProgressEvents); | ||
}, | ||
set: function(val) { _minProgressEvents = val } | ||
}); | ||
Object.defineProperty(this, "randomness", { | ||
get: function() { | ||
return Math.max(_randomness, XHRShaper.randomness); | ||
}, | ||
set: function(val) { _randomness = val } | ||
}); | ||
}; | ||
@@ -46,181 +46,193 @@ | ||
function mirrorReadOnlyProp(target, source, prop) { | ||
Object.defineProperty(target, prop, { | ||
get: function() { | ||
return source[prop]; | ||
} | ||
}); | ||
Object.defineProperty(target, prop, { | ||
get: function() { | ||
return source[prop]; | ||
} | ||
}); | ||
} | ||
function mirrorRwProp(target, source, prop) { | ||
Object.defineProperty(target, prop, { | ||
get: function() { | ||
return source[prop]; | ||
}, | ||
set: function(val) { | ||
source[prop] = val; | ||
} | ||
}); | ||
Object.defineProperty(target, prop, { | ||
get: function() { | ||
return source[prop]; | ||
}, | ||
set: function(val) { | ||
source[prop] = val; | ||
} | ||
}); | ||
} | ||
function mirrorFunc(target, source, func) { | ||
//if (!source[func]) return; // in case someone doesn't implement the full standard bind() will fail here | ||
target[func] = source[func].bind(source); | ||
//if (!source[func]) return; // in case someone doesn't implement the full standard bind() will fail here | ||
target[func] = source[func].bind(source); | ||
} | ||
var XMLHttpRequest = function() { | ||
var xhr = new WindowXHR(); | ||
var shaper = new XHRShaper(); | ||
var _onreadystatechange, _onprogress, _onloadend; | ||
var _this = { | ||
shaper: shaper | ||
}; | ||
var xhr = new WindowXHR(); | ||
var shaper = new XHRShaper(); | ||
var _onreadystatechange, _onprogress, _onloadend; | ||
var _this = { | ||
shaper: shaper | ||
}; | ||
var openedTs, headersTs, loadingTs, doneTs; | ||
var loaded = 0, total; | ||
var currentBitrateKpbs; | ||
var progressEvents = []; | ||
var progressTimer; | ||
var lastProgressEvent = false; | ||
var loadEndEvent; | ||
var openedTs, headersTs, loadingTs, doneTs; | ||
var loaded = 0, total; | ||
var currentBitrateKpbs; | ||
var progressEvents = []; | ||
var progressTimer; | ||
var lastProgressEvent = false; | ||
var loadEndEvent; | ||
xhr.onloadend = function(event) { | ||
loadEndEvent = event; | ||
}; | ||
xhr.onloadend = function(event) { | ||
loadEndEvent = event; | ||
xhr.onreadystatechange = function(event) { | ||
if (_onreadystatechange) { | ||
switch (xhr.readyState) { | ||
case 0: // UNSENT | ||
_onreadystatechange(event); | ||
break; | ||
case 1: // OPENED | ||
openedTs = Date.now(); | ||
_onreadystatechange(event); | ||
break; | ||
case 2: // HEADERS_RECEIVE | ||
headersTs = Date.now(); | ||
_onreadystatechange(event); | ||
break; | ||
case 3: // LOADING | ||
loadingTs = Date.now(); | ||
_onreadystatechange(event); | ||
break; | ||
case 4: // DONE | ||
var delay1 = 0, delay2 = 0; | ||
doneTs = Date.now(); | ||
var latency = doneTs - openedTs; | ||
if (latency < shaper.minLatency) { | ||
delay1 = shaper.minLatency - latency; | ||
} | ||
if (currentBitrateKpbs > shaper.maxBandwidth) { | ||
delay2 = (currentBitrateKpbs / shaper.maxBandwidth) * latency - latency; | ||
} | ||
if (delay1 || delay2) { | ||
setTimeout(function() { | ||
if (_onloadend && xhr.readyState === 4) { | ||
_onloadend(event); | ||
} | ||
}; | ||
if (loaded === total && !lastProgressEvent) { | ||
clearTimeout(progressTimer); | ||
_onprogress(progressEvents[progressEvents.length-1]); | ||
} | ||
xhr.onreadystatechange = function(event) { | ||
_onreadystatechange(event); | ||
function triggerStateChange(e) { | ||
if (_onreadystatechange) { | ||
_onreadystatechange(e); | ||
} | ||
} | ||
if (loadEndEvent && _onloadend) { | ||
_onloadend(loadEndEvent); | ||
} | ||
switch (xhr.readyState) { | ||
case 0: // UNSENT | ||
triggerStateChange(event); | ||
break; | ||
case 1: // OPENED | ||
openedTs = Date.now(); | ||
triggerStateChange(event); | ||
break; | ||
case 2: // HEADERS_RECEIVE | ||
headersTs = Date.now(); | ||
triggerStateChange(event); | ||
break; | ||
case 3: // LOADING | ||
loadingTs = Date.now(); | ||
triggerStateChange(event); | ||
break; | ||
case 4: // DONE | ||
var delay1 = 0, delay2 = 0; | ||
doneTs = Date.now(); | ||
var latency = doneTs - openedTs; | ||
if (latency < shaper.minLatency) { | ||
delay1 = shaper.minLatency - latency; | ||
} | ||
if (currentBitrateKpbs > shaper.maxBandwidth) { | ||
delay2 = (currentBitrateKpbs / shaper.maxBandwidth) * latency - latency; | ||
} | ||
if (delay1 || delay2) { | ||
setTimeout(function() { | ||
}, Math.max(delay1, delay2)); | ||
break; | ||
} | ||
_onreadystatechange(event); | ||
break; | ||
} | ||
} | ||
}; | ||
if (loaded === total && !lastProgressEvent) { | ||
clearTimeout(progressTimer); | ||
_onprogress(progressEvents[progressEvents.length-1]); | ||
} | ||
xhr.onprogress = function(event) { | ||
triggerStateChange(event); | ||
function triggerProgress(e) { | ||
if (loadEndEvent && _onloadend) { | ||
_onloadend(loadEndEvent); | ||
} | ||
if (loaded === total) { | ||
lastProgressEvent = true; | ||
} | ||
}, Math.max(delay1, delay2)); | ||
break; | ||
} | ||
if (_onprogress) { | ||
_onprogress(e); | ||
} | ||
} | ||
triggerStateChange(event); | ||
var now = Date.now(); | ||
var duration = now - openedTs; | ||
var delay; | ||
break; | ||
} | ||
loaded = event.loaded; | ||
total = event.total; | ||
currentBitrateKpbs = 8 * loaded / duration; // kbps | ||
}; | ||
//console.log('current bitrate: ' + Math.round(currentBitrateKpbs) + ' kbps'); | ||
xhr.onprogress = function(event) { | ||
if (currentBitrateKpbs > shaper.maxBandwidth) { | ||
delay = (currentBitrateKpbs / shaper.maxBandwidth) * duration - duration; | ||
progressEvents.push(event); | ||
//console.log('delaying progress event by ' + Math.round(delay) + ' ms'); | ||
progressTimer = setTimeout(function() { | ||
triggerProgress(event); | ||
}, delay); | ||
return; | ||
} | ||
function triggerProgress(e) { | ||
triggerProgress(event); | ||
}; | ||
if (loaded === total) { | ||
lastProgressEvent = true; | ||
} | ||
Object.defineProperty(_this, "onreadystatechange", { | ||
get: function() { | ||
return xhr.onreadystatechange; | ||
}, | ||
set: function(handler) { | ||
_onreadystatechange = handler; | ||
} | ||
}); | ||
if (_onprogress) { | ||
_onprogress(e); | ||
} | ||
} | ||
Object.defineProperty(_this, "onprogress", { | ||
get: function() { | ||
return xhr.onprogress; | ||
}, | ||
set: function(handler) { | ||
_onprogress = handler; | ||
} | ||
}); | ||
var now = Date.now(); | ||
var duration = now - openedTs; | ||
var delay; | ||
Object.defineProperty(_this, "onloadend", { | ||
get: function() { | ||
return xhr.onloadend; | ||
}, | ||
set: function(handler) { | ||
_onloadend = handler; | ||
} | ||
}); | ||
loaded = event.loaded; | ||
total = event.total; | ||
currentBitrateKpbs = 8 * loaded / duration; // kbps | ||
mirrorRwProp(_this, xhr, "responseType"); | ||
mirrorRwProp(_this, xhr, "timeout"); | ||
mirrorRwProp(_this, xhr, "withCredentials"); | ||
//console.log('current bitrate: ' + Math.round(currentBitrateKpbs) + ' kbps'); | ||
mirrorReadOnlyProp(_this, xhr, "readyState"); | ||
mirrorReadOnlyProp(_this, xhr, "response"); | ||
mirrorReadOnlyProp(_this, xhr, "responseText"); | ||
mirrorReadOnlyProp(_this, xhr, "responseURL"); | ||
mirrorReadOnlyProp(_this, xhr, "responseXML"); | ||
mirrorReadOnlyProp(_this, xhr, "status"); | ||
mirrorReadOnlyProp(_this, xhr, "statusText"); | ||
mirrorReadOnlyProp(_this, xhr, "upload"); | ||
if (currentBitrateKpbs > shaper.maxBandwidth) { | ||
delay = (currentBitrateKpbs / shaper.maxBandwidth) * duration - duration; | ||
progressEvents.push(event); | ||
//console.log('delaying progress event by ' + Math.round(delay) + ' ms'); | ||
progressTimer = setTimeout(function() { | ||
triggerProgress(event); | ||
}, delay); | ||
return; | ||
} | ||
mirrorFunc(_this, xhr, "abort"); | ||
mirrorFunc(_this, xhr, "open"); | ||
mirrorFunc(_this, xhr, "send"); | ||
mirrorFunc(_this, xhr, "setRequestHeader"); | ||
mirrorFunc(_this, xhr, "getResponseHeader"); | ||
mirrorFunc(_this, xhr, "overrideMimeType"); | ||
mirrorFunc(_this, xhr, "getAllResponseHeaders"); | ||
triggerProgress(event); | ||
}; | ||
return _this; | ||
Object.defineProperty(_this, "onreadystatechange", { | ||
get: function() { | ||
return xhr.onreadystatechange; | ||
}, | ||
set: function(handler) { | ||
_onreadystatechange = handler; | ||
} | ||
}); | ||
Object.defineProperty(_this, "onprogress", { | ||
get: function() { | ||
return xhr.onprogress; | ||
}, | ||
set: function(handler) { | ||
_onprogress = handler; | ||
} | ||
}); | ||
Object.defineProperty(_this, "onloadend", { | ||
get: function() { | ||
return xhr.onloadend; | ||
}, | ||
set: function(handler) { | ||
_onloadend = handler; | ||
} | ||
}); | ||
mirrorRwProp(_this, xhr, "responseType"); | ||
mirrorRwProp(_this, xhr, "timeout"); | ||
mirrorRwProp(_this, xhr, "withCredentials"); | ||
mirrorReadOnlyProp(_this, xhr, "readyState"); | ||
mirrorReadOnlyProp(_this, xhr, "response"); | ||
mirrorReadOnlyProp(_this, xhr, "responseText"); | ||
mirrorReadOnlyProp(_this, xhr, "responseURL"); | ||
mirrorReadOnlyProp(_this, xhr, "responseXML"); | ||
mirrorReadOnlyProp(_this, xhr, "status"); | ||
mirrorReadOnlyProp(_this, xhr, "statusText"); | ||
mirrorReadOnlyProp(_this, xhr, "upload"); | ||
mirrorFunc(_this, xhr, "abort"); | ||
mirrorFunc(_this, xhr, "open"); | ||
mirrorFunc(_this, xhr, "send"); | ||
mirrorFunc(_this, xhr, "setRequestHeader"); | ||
mirrorFunc(_this, xhr, "getResponseHeader"); | ||
mirrorFunc(_this, xhr, "overrideMimeType"); | ||
mirrorFunc(_this, xhr, "getAllResponseHeaders"); | ||
return _this; | ||
}; | ||
@@ -232,3 +244,3 @@ | ||
if (typeof module !== 'undefined') { | ||
module.exports = XMLHttpRequest; | ||
module.exports = XMLHttpRequest; | ||
} | ||
@@ -235,0 +247,0 @@ |
{ | ||
"name": "xhr-shaper", | ||
"version": "0.2.0", | ||
"version": "0.2.1", | ||
"description": "Shapes your XHR requests to a max emulated bandwidth and latency, randomizes frequency of progress events", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
12363
205