Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

xhr-shaper

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

xhr-shaper - npm Package Compare versions

Comparing version 2.2.1 to 2.2.2

src/cache.js

133

demo.js

@@ -27,4 +27,6 @@ XHRShaper.useGlobal();

stats.timestamps.push(Date.now() - stats.t0);
stats.bytes.push(bytes);
stats.timestamps.push(Date.now() - stats.t0);
stats.bytes.push(bytes);
console.log('stats pushed:', stats);
}

@@ -38,15 +40,17 @@

Plotly.plot(el, [{
x: stats.timestamps,
y: stats.bytes
Plotly.plot(
el,
[{
x: stats.timestamps,
y: stats.bytes
}],
{
margin: { t: 0 }
}
);
margin: { t: 0 }
}
);
document.querySelector('#throughput').innerHTML = stats.throughput;
document.querySelector('#duration').innerHTML = stats.totalDuration;
document.querySelector('#throughput').innerHTML = stats.throughput;
document.querySelector('#duration').innerHTML = stats.totalDuration;
document.querySelector('#status').innerHTML = 'Ready';
document.querySelector('#status').innerHTML = 'Ready';
}

@@ -79,76 +83,81 @@

function makeRequest(url, done, minLatency, maxBandwidth) {
var loaded, total;
function makeRequest(url, onRequestDone, minLatency, maxBandwidth) {
var loaded, total;
console.log('url:', url);
console.log('url:', url);
requestActive = true;
requestActive = true;
var xhr = new XMLHttpRequest();
var xhr = new XMLHttpRequest();
xhr.shaper.minLatency = minLatency;
xhr.shaper.maxBandwidth = maxBandwidth;
xhr.caching = false;
xhr.onreadystatechange = function(e) {
console.log('readyState: ' + xhr.readyState);
xhr.shaper.minLatency = minLatency;
xhr.shaper.maxBandwidth = maxBandwidth;
if (xhr.readyState === 4) {
doneTime = Date.now();
var duration = doneTime - reqTime;
var bitrate = Math.round(8 * total / duration);
xhr.onreadystatechange = function(e) {
console.log('readyState changed: ' + xhr.readyState);
stats.totalDuration = duration;
stats.throughput = bitrate;
if (xhr.readyState === 4) {
doneTime = Date.now();
var duration = doneTime - reqTime;
var bitrate = Math.round(8 * total / duration);
console.log('Loaded ' + total + ' bytes in ' + duration + ' ms, computed bitrate: ' + bitrate + ' kbps');
}
};
xhr.onprogress = function(e) {
loaded = e.loaded;
total = e.total;
stats.totalDuration = duration;
stats.throughput = bitrate;
pushStats(e.loaded, e.total);
console.log('Loaded ' + total + ' bytes in ' + duration + ' ms, computed bitrate: ' + bitrate + ' kbps');
}
};
xhr.onprogress = function(e) {
loaded = e.loaded;
total = e.total;
console.log('Progress: ' + e.loaded + ' of ' + e.total);
};
pushStats(e.loaded, e.total);
xhr.onload = function(e) {
console.log('Progress: ' + e.loaded + ' of ' + e.total);
};
if (xhr.readyState < 4) {
console.warn('onload called with readyState:', xhr.readyState, ' id:', xhr.id);
return;
}
xhr.onload = function(e) {
console.log('Loading done');
//console.log(e);
if (xhr.readyState < 4) {
console.warn('onload called with readyState:', xhr.readyState, ' id:', xhr.id);
return;
}
console.log('readyState:', xhr.readyState);
console.log('Loading done');
//console.log(e);
if (xhr.response.byteLength < 1024) {
console.log(new TextDecoder("utf-8").decode(xhr.response));
}
console.log('readyState:', xhr.readyState);
done(xhr);
}
if (xhr.response.byteLength < 1024) {
console.log(new TextDecoder("utf-8").decode(xhr.response));
}
xhr.onloadend = function(e) {
console.log('Loading ended');
//console.log(e);
}
xhr.withCredentials = false;
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
}
console.debug('Max bandwidth: ' + xhr.shaper.maxBandwidth);
console.debug('Min latency: ' + xhr.shaper.minLatency);
xhr.onloadend = function(e) {
console.log('Loading ended');
var reqTime = Date.now(), doneTime;
pushStats(loaded, total);
xhr.send();
onRequestDone(xhr);
}
resetStats();
pushStats(0, 0);
xhr.withCredentials = false;
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
return xhr;
console.debug('Max bandwidth: ' + xhr.shaper.maxBandwidth);
console.debug('Min latency: ' + xhr.shaper.minLatency);
var reqTime = Date.now(), doneTime;
xhr.send();
resetStats();
pushStats(0, 0);
return xhr;
}

@@ -304,14 +304,22 @@ this["XHRShaper"] =

var _xhr = __webpack_require__(0);
var _xhrProxy = __webpack_require__(0);
var _xhr2 = _interopRequireDefault(_xhr);
var _xhrProxy2 = _interopRequireDefault(_xhrProxy);
var _shaper = __webpack_require__(4);
var _shaper = __webpack_require__(6);
var _shaper2 = _interopRequireDefault(_shaper);
var _setupThrottledXhr = __webpack_require__(3);
var _setupThrottledXhr = __webpack_require__(5);
var _setupThrottledXhr2 = _interopRequireDefault(_setupThrottledXhr);
var _setupCachedXhr = __webpack_require__(4);
var _setupCachedXhr2 = _interopRequireDefault(_setupCachedXhr);
var _cache = __webpack_require__(3);
var _cache2 = _interopRequireDefault(_cache);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -325,2 +333,4 @@

var DEBUG = false;
var PASSTHROUGH_EVENTS = ['loadstart', 'timeout', 'abort', 'error'];

@@ -334,6 +344,6 @@

var ThrottledXHR = function (_XHRProxy) {
_inherits(ThrottledXHR, _XHRProxy);
var XHR = function (_XHRProxy) {
_inherits(XHR, _XHRProxy);
_createClass(ThrottledXHR, null, [{
_createClass(XHR, null, [{
key: 'Shaper',

@@ -343,13 +353,35 @@ get: function get() {

}
}, {
key: 'cache',
get: function get() {
return _cache2.default;
}
}]);
function ThrottledXHR() {
_classCallCheck(this, ThrottledXHR);
function XHR() {
_classCallCheck(this, XHR);
var _this = _possibleConstructorReturn(this, (ThrottledXHR.__proto__ || Object.getPrototypeOf(ThrottledXHR)).call(this));
var _this = _possibleConstructorReturn(this, (XHR.__proto__ || Object.getPrototypeOf(XHR)).call(this));
_this._cacheInstance = _cache2.default.instance;
_this._shaper = new _shaper2.default();
_this._listenersMap = new Map();
_this._dispatchedEventsList = [];
_this._response = null;
_this._responseText = null;
_this._responseXML = null;
_this._headers = null;
_this._readyState = 0;
_this._caching = false;
_this._cacheHit = false;
_this._cacheWrite = true;
_this._onSend = null;
_this.addEventListener('loadend', function () {
if (_this._caching && _this._cacheWrite) {
DEBUG && console.log('CACHE WRITE:', _this._xhr.responseURL);
_this._cacheInstance.put(_this._xhr.responseURL, _this._response, _this._responseText, _this._responseXML, null, _this._xhr.getAllResponseHeaders());
}
});
(0, _setupThrottledXhr2.default)(_this._xhr, _this);

@@ -359,3 +391,3 @@ return _this;

_createClass(ThrottledXHR, [{
_createClass(XHR, [{
key: '_dispatchWrappedEventType',

@@ -377,2 +409,63 @@ value: function _dispatchWrappedEventType(type) {

}, {
key: '_handleCacheHitOnSend',
value: function _handleCacheHitOnSend(cachedResource) {
var _this3 = this;
this._readyState = 1;
this._cacheHit = true;
var onSend = function onSend() {
setTimeout(function () {
_this3._readyState = 2;
_this3.onreadystatechange && _this3.onreadystatechange();
_this3._dispatchWrappedEventType('readystatechange');
_this3._readyState = 3;
_this3.onreadystatechange && _this3.onreadystatechange();
_this3._dispatchWrappedEventType('readystatechange');
_this3.onloadstart && _this3.onloadstart();
_this3._dispatchWrappedEventType('loadstart');
_this3._response = cachedResource.data;
_this3._responseText = cachedResource.dataText;
_this3._responseXML = cachedResource.dataXML;
_this3._headersAll = cachedResource.headers;
_this3._readyState = 4;
_this3.onreadystatechange && _this3.onreadystatechange();
_this3._dispatchWrappedEventType('readystatechange');
_this3.onreadystatechange && _this3.onreadystatechange();
_this3._dispatchWrappedEventType('readystatechange');
_this3.onload && _this3.onload();
_this3._dispatchWrappedEventType('load');
_this3.onloadend && _this3.onloadend();
_this3._dispatchWrappedEventType('loadend');
}, 0);
};
this._onSend = onSend;
}
}, {
key: '_setupWrappedResponseData',
value: function _setupWrappedResponseData() {
try {
this._response = this._xhr.response;
} catch (e) {
DEBUG && console.warn(e);
}
try {
this._responseText = this._xhr.responseText;
} catch (e) {
DEBUG && console.warn(e);
}
try {
this._responseXML = this._xhr.responseXML;
} catch (e) {
DEBUG && console.warn(e);
}
}
}, {
key: '_setupWrappedHeaders',
value: function _setupWrappedHeaders() {
try {
this._headersAll = this._xhr.getAllResponseHeaders();
} catch (e) {}
}
}, {
key: 'addEventListener',

@@ -382,3 +475,3 @@ value: function addEventListener(type, listener, optionsOrUseCapture, wantsUntrusted) {

if (PASSTHROUGH_EVENTS.includes(type)) {
return _get(ThrottledXHR.prototype.__proto__ || Object.getPrototypeOf(ThrottledXHR.prototype), 'addEventListener', this).call(this, type, listener, optionsOrUseCapture, wantsUntrusted);
return _get(XHR.prototype.__proto__ || Object.getPrototypeOf(XHR.prototype), 'addEventListener', this).call(this, type, listener, optionsOrUseCapture, wantsUntrusted);
}

@@ -388,3 +481,3 @@

this._listenersMap.set(listener, listenerWrapper);
return _get(ThrottledXHR.prototype.__proto__ || Object.getPrototypeOf(ThrottledXHR.prototype), 'addEventListener', this).call(this, type, listenerWrapper, optionsOrUseCapture, wantsUntrusted);
return _get(XHR.prototype.__proto__ || Object.getPrototypeOf(XHR.prototype), 'addEventListener', this).call(this, type, listenerWrapper, optionsOrUseCapture, wantsUntrusted);
}

@@ -396,3 +489,3 @@ }, {

if (PASSTHROUGH_EVENTS.includes(type)) {
return _get(ThrottledXHR.prototype.__proto__ || Object.getPrototypeOf(ThrottledXHR.prototype), 'removeEventListener', this).call(this, type, listener, optionsOrUseCapture);
return _get(XHR.prototype.__proto__ || Object.getPrototypeOf(XHR.prototype), 'removeEventListener', this).call(this, type, listener, optionsOrUseCapture);
}

@@ -405,10 +498,70 @@

this._listenersMap.delete(listener);
return _get(ThrottledXHR.prototype.__proto__ || Object.getPrototypeOf(ThrottledXHR.prototype), 'removeEventListener', this).call(this, type, listenerWrapper, optionsOrUseCapture);
return _get(XHR.prototype.__proto__ || Object.getPrototypeOf(XHR.prototype), 'removeEventListener', this).call(this, type, listenerWrapper, optionsOrUseCapture);
}
}, {
key: 'dispatchEvent',
value: function dispatchEvent(event) {
return _get(ThrottledXHR.prototype.__proto__ || Object.getPrototypeOf(ThrottledXHR.prototype), 'dispatchEvent', this).call(this, event);
key: 'open',
value: function open(method, url, async, user, password) {
if (this._caching) {
if (!this._cacheInstance) {
throw new Error('no cache setup');
}
DEBUG && console.log('CACHE GET:', url);
var cachedResource = this._cacheInstance.get(url, false);
if (cachedResource) {
DEBUG && console.log('CACHE HIT');
this._handleCacheHitOnSend(cachedResource);
}
}
return _get(XHR.prototype.__proto__ || Object.getPrototypeOf(XHR.prototype), 'open', this).call(this, method, url, async, user, password);
}
}, {
key: 'send',
value: function send(data) {
if (this._caching && this._cacheHit && this._onSend) {
return this._onSend();
}
return _get(XHR.prototype.__proto__ || Object.getPrototypeOf(XHR.prototype), 'send', this).call(this, data);
}
}, {
key: 'getAllResponseHeaders',
value: function getAllResponseHeaders() {
return this._headersAll;
}
}, {
key: 'caching',
get: function get() {
return this._caching;
},
set: function set(enabled) {
this._caching = enabled;
}
}, {
key: 'cacheInstance',
set: function set(instance) {
this._cacheInstance = instance;
},
get: function get() {
return this._cacheInstance;
}
}, {
key: 'isCacheHit',
get: function get() {
return this._cacheHit;
}
}, {
key: 'cacheWriteEnabled',
set: function set(enable) {
this._cacheWrite = enable;
}
}, {
key: 'enableCacheWrite',
get: function get() {
return this._cacheWrite;
}
}, {
key: 'shaper',

@@ -419,2 +572,9 @@ get: function get() {

}, {
key: 'readyState',
get: function get() {
if (typeof this._readyState === 'number') {
return this._readyState;
}
}
}, {
key: 'onloadend',

@@ -451,8 +611,23 @@ set: function set(fn) {

}
}, {
key: 'response',
get: function get() {
return this._response;
}
}, {
key: 'responseText',
get: function get() {
return this._responseText;
}
}, {
key: 'responseXML',
get: function get() {
return this._responseXML;
}
}]);
return ThrottledXHR;
}(_xhr2.default);
return XHR;
}(_xhrProxy2.default);
exports.default = ThrottledXHR;
exports.default = XHR;

@@ -466,9 +641,9 @@ /***/ }),

var _xhr = __webpack_require__(0);
var _xhrProxy = __webpack_require__(0);
var _xhr2 = _interopRequireDefault(_xhr);
var _xhrProxy2 = _interopRequireDefault(_xhrProxy);
var _throttledXhr = __webpack_require__(1);
var _xhr = __webpack_require__(1);
var _throttledXhr2 = _interopRequireDefault(_throttledXhr);
var _xhr2 = _interopRequireDefault(_xhr);

@@ -481,9 +656,8 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

// Overload native window constructor
global.XMLHttpRequest = _throttledXhr2.default;
global.XMLHttpRequest = _xhr2.default;
}
module.exports = {
XMLHttpRequest: _throttledXhr2.default,
ThrottledXHR: _throttledXhr2.default,
XHRProxy: _xhr2.default,
XMLHttpRequest: _xhr2.default,
XHRProxy: _xhrProxy2.default,
useGlobal: useGlobal

@@ -500,4 +674,169 @@ };

Object.defineProperty(exports, "__esModule", {
value: true
});
var DEFAULT_ALLOW_UPDATES = false;
var MAX_CACHE_SIZE_BYTES = 1024 * 1e6; // 1024 Mbytes
var cache = new Map();
var bytesRead = 0;
var bytesWritten = 0;
var misses = 0;
var hits = 0;
var instance = {
allowUpdates: DEFAULT_ALLOW_UPDATES,
errorOnOverflow: false,
get: function get(uri) {
var onlyData = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var resource = void 0;
if (!cache.has(uri)) {
misses++;
return null;
}
hits++;
resource = cache.get(uri);
resource.accessedAt = Date.now();
if (typeof resource.data.byteLength === 'number') {
bytesRead += resource.data.byteLength;
}
if (onlyData) {
return resource.data;
} else {
return resource;
}
},
put: function put(uri, data, dataText, dataXML, headers, headersAll) {
if (!instance.allowUpdates && cache.has(uri)) {
throw new Error('Cache updates not allowed. Purge first! URI:', uri);
}
var createdAt = Date.now();
var accessedAt = null;
var resource = {
uri: uri,
data: data,
dataText: dataText,
dataXML: dataXML,
headers: headers,
headersAll: headersAll,
createdAt: createdAt,
accessedAt: accessedAt
};
cache.set(uri, resource);
if (typeof resource.data.byteLength === 'number') {
bytesWritten += resource.data.byteLength;
}
var totalSize = instance.countBytes();
if (totalSize > MAX_CACHE_SIZE_BYTES) {
if (instance.errorOnOverflow) throw new Error('Cache exceeds max size, has', totalSize, 'bytes');
instance.purgeOldest();
}
return instance;
},
purgeByUri: function purgeByUri(uri) {
return cache.delete(uri);
},
purgeAll: function purgeAll() {
cache.clear();
},
purgeNotAccessedSince: function purgeNotAccessedSince(timeMillisSince) {
var now = Date.now();
cache.forEach(function (resource, uri) {
if (!resource.accessedAt // never accessed
|| resource.accessedAt < now - timeMillisSince) cache.delete(uri);
});
},
purgeCreatedBefore: function purgeCreatedBefore(timestamp) {
cache.forEach(function (resource, uri) {
if (createdAt < timestamp) cache.delete(uri);
});
},
purgeOldest: function purgeOldest() {
var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'accessed';
var count = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
var prop = type + 'At';
var _loop = function _loop(i) {
var oldest = null;
cache.forEach(function (resource) {
if (!oldest || resource[prop] < oldest[prop]) {
oldest = resource;
}
});
cache.delete(oldest.uri);
};
for (var i = 0; i < count; i++) {
_loop(i);
}
},
reduce: function reduce(reduceFn) {
var accuInit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
var accu = accuInit;
cache.forEach(function (resource, uri) {
accu = reduceFn.bind(undefined)(accu, resource);
});
return accu;
},
sumDataProperty: function sumDataProperty(field) {
return instance.reduce(function (accu, resource) {
return accu + resource.data[field];
});
},
countBytes: function countBytes() {
return instance.sumDataProperty('byteLength');
}
};
var getInfo = function getInfo() {
return {
bytesRead: bytesRead,
bytesWritten: bytesWritten,
hits: hits,
misses: misses
};
};
exports.default = {
getInfo: getInfo,
instance: instance
};
/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function setupCachedXhr(xhr, xhrProxy, cacheInstance) {
xhrProxy._cacheInstance = cacheInstance;
}
exports.default = setupCachedXhr;
/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var XHRReadyStates = {
UNSENT: 0,
OPENED: 1,
HEADERS_RECEIVED: 2,
LOADING: 3,
DONE: 4
};
function setupThrottledXhr(xhr, xhrProxy) {

@@ -511,10 +850,10 @@ var shaper = xhrProxy.shaper;

doneTs = void 0;
var loaded = 0,
total = void 0;
var loaded = 0;
var total = 0;
var currentBitrateKpbs = void 0;
var progressEvents = [];
var progressTimer = void 0;
var progressTimer = null;
var lastProgressEvent = false;
var loadEndEvent = void 0;
var loadEvent = void 0;
var loadEndEvent = null;
var loadEvent = null;
var done = false;

@@ -525,3 +864,2 @@

//console.log('native loadend');

@@ -541,3 +879,4 @@ loadEndEvent = event;

loadEvent = event;
if (done && xhr.readyState === 4) {
if (done && xhr.readyState === XHRReadyStates.DONE) {
xhrProxy._setupWrappedResponseData();
_onload && _onload(event);

@@ -549,2 +888,3 @@ xhrProxy._dispatchWrappedEventType('load');

xhr.onreadystatechange = function (event) {
var now = Date.now();
var _onreadystatechange = xhrProxy._onreadystatechange,

@@ -555,34 +895,41 @@ _onprogress = xhrProxy._onprogress,

var triggerStateChange = function triggerStateChange(e, readyState) {
if (typeof readyState !== 'number') {
throw new Error('readyState should be a number');
}
function triggerStateChange(e) {
xhrProxy._readyState = readyState;
_onreadystatechange && _onreadystatechange(e);
xhrProxy._dispatchWrappedEventType('readystatechange');
}
};
var latency = void 0;
var delay1 = 0;
var delay2 = 0;
switch (xhr.readyState) {
case 0:
// UNSENT
triggerStateChange(event);
triggerStateChange(event, XHRReadyStates.UNSENT);
break;
case 1:
// OPENED
openedTs = Date.now();
triggerStateChange(event);
openedTs = now;
triggerStateChange(event, XHRReadyStates.OPENED);
break;
case 2:
// HEADERS_RECEIVE
headersTs = Date.now();
triggerStateChange(event);
// HEADERS_RECEIVED
headersTs = now;
xhrProxy._setupWrappedHeaders();
triggerStateChange(event, XHRReadyStates.HEADERS_RECEIVED);
break;
case 3:
// LOADING
loadingTs = Date.now();
triggerStateChange(event);
loadingTs = now;
triggerStateChange(event, XHRReadyStates.LOADING);
break;
case 4:
// DONE
var delay1 = 0,
delay2 = 0;
doneTs = Date.now();
var latency = doneTs - openedTs;
doneTs = now;
latency = doneTs - openedTs;
if (latency < shaper.minLatency) {

@@ -603,3 +950,3 @@ delay1 = shaper.minLatency - latency;

triggerStateChange(event);
triggerStateChange(event, XHRReadyStates.DONE);

@@ -609,2 +956,3 @@ done = true;

if (loadEvent) {
xhrProxy._setupWrappedResponseData();
_onload && _onload(loadEvent);

@@ -624,3 +972,4 @@ xhrProxy._dispatchWrappedEventType('load');

done = true;
triggerStateChange(event);
xhrProxy._setupWrappedResponseData();
triggerStateChange(event, XHRReadyStates.DONE);
}

@@ -632,7 +981,6 @@ break;

xhr.onprogress = function (event) {
var now = Date.now();
var _onprogress = xhrProxy._onprogress;
function triggerProgress(e) {
var triggerProgress = function triggerProgress(e) {
if (loaded === total) {

@@ -644,5 +992,4 @@ lastProgressEvent = true;

xhrProxy._dispatchWrappedEventType('progress');
}
};
var now = Date.now();
var duration = now - openedTs;

@@ -655,8 +1002,5 @@ var delay = void 0;

// console.log('current bitrate: ' + Math.round(currentBitrateKpbs) + ' kbps');
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 () {

@@ -670,7 +1014,2 @@ triggerProgress(event);

};
var id = Math.round(Math.random() * 1e6);
xhr.__throttledId = id;
xhrProxy.__throttledId = id;
}

@@ -681,3 +1020,3 @@

/***/ }),
/* 4 */
/* 6 */
/***/ (function(module, exports, __webpack_require__) {

@@ -684,0 +1023,0 @@

@@ -313,14 +313,22 @@ (function webpackUniversalModuleDefinition(root, factory) {

var _xhr = __webpack_require__(0);
var _xhrProxy = __webpack_require__(0);
var _xhr2 = _interopRequireDefault(_xhr);
var _xhrProxy2 = _interopRequireDefault(_xhrProxy);
var _shaper = __webpack_require__(4);
var _shaper = __webpack_require__(6);
var _shaper2 = _interopRequireDefault(_shaper);
var _setupThrottledXhr = __webpack_require__(3);
var _setupThrottledXhr = __webpack_require__(5);
var _setupThrottledXhr2 = _interopRequireDefault(_setupThrottledXhr);
var _setupCachedXhr = __webpack_require__(4);
var _setupCachedXhr2 = _interopRequireDefault(_setupCachedXhr);
var _cache = __webpack_require__(3);
var _cache2 = _interopRequireDefault(_cache);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -334,2 +342,4 @@

var DEBUG = false;
var PASSTHROUGH_EVENTS = ['loadstart', 'timeout', 'abort', 'error'];

@@ -343,6 +353,6 @@

var ThrottledXHR = function (_XHRProxy) {
_inherits(ThrottledXHR, _XHRProxy);
var XHR = function (_XHRProxy) {
_inherits(XHR, _XHRProxy);
_createClass(ThrottledXHR, null, [{
_createClass(XHR, null, [{
key: 'Shaper',

@@ -352,13 +362,35 @@ get: function get() {

}
}, {
key: 'cache',
get: function get() {
return _cache2.default;
}
}]);
function ThrottledXHR() {
_classCallCheck(this, ThrottledXHR);
function XHR() {
_classCallCheck(this, XHR);
var _this = _possibleConstructorReturn(this, (ThrottledXHR.__proto__ || Object.getPrototypeOf(ThrottledXHR)).call(this));
var _this = _possibleConstructorReturn(this, (XHR.__proto__ || Object.getPrototypeOf(XHR)).call(this));
_this._cacheInstance = _cache2.default.instance;
_this._shaper = new _shaper2.default();
_this._listenersMap = new Map();
_this._dispatchedEventsList = [];
_this._response = null;
_this._responseText = null;
_this._responseXML = null;
_this._headers = null;
_this._readyState = 0;
_this._caching = false;
_this._cacheHit = false;
_this._cacheWrite = true;
_this._onSend = null;
_this.addEventListener('loadend', function () {
if (_this._caching && _this._cacheWrite) {
DEBUG && console.log('CACHE WRITE:', _this._xhr.responseURL);
_this._cacheInstance.put(_this._xhr.responseURL, _this._response, _this._responseText, _this._responseXML, null, _this._xhr.getAllResponseHeaders());
}
});
(0, _setupThrottledXhr2.default)(_this._xhr, _this);

@@ -368,3 +400,3 @@ return _this;

_createClass(ThrottledXHR, [{
_createClass(XHR, [{
key: '_dispatchWrappedEventType',

@@ -386,2 +418,63 @@ value: function _dispatchWrappedEventType(type) {

}, {
key: '_handleCacheHitOnSend',
value: function _handleCacheHitOnSend(cachedResource) {
var _this3 = this;
this._readyState = 1;
this._cacheHit = true;
var onSend = function onSend() {
setTimeout(function () {
_this3._readyState = 2;
_this3.onreadystatechange && _this3.onreadystatechange();
_this3._dispatchWrappedEventType('readystatechange');
_this3._readyState = 3;
_this3.onreadystatechange && _this3.onreadystatechange();
_this3._dispatchWrappedEventType('readystatechange');
_this3.onloadstart && _this3.onloadstart();
_this3._dispatchWrappedEventType('loadstart');
_this3._response = cachedResource.data;
_this3._responseText = cachedResource.dataText;
_this3._responseXML = cachedResource.dataXML;
_this3._headersAll = cachedResource.headers;
_this3._readyState = 4;
_this3.onreadystatechange && _this3.onreadystatechange();
_this3._dispatchWrappedEventType('readystatechange');
_this3.onreadystatechange && _this3.onreadystatechange();
_this3._dispatchWrappedEventType('readystatechange');
_this3.onload && _this3.onload();
_this3._dispatchWrappedEventType('load');
_this3.onloadend && _this3.onloadend();
_this3._dispatchWrappedEventType('loadend');
}, 0);
};
this._onSend = onSend;
}
}, {
key: '_setupWrappedResponseData',
value: function _setupWrappedResponseData() {
try {
this._response = this._xhr.response;
} catch (e) {
DEBUG && console.warn(e);
}
try {
this._responseText = this._xhr.responseText;
} catch (e) {
DEBUG && console.warn(e);
}
try {
this._responseXML = this._xhr.responseXML;
} catch (e) {
DEBUG && console.warn(e);
}
}
}, {
key: '_setupWrappedHeaders',
value: function _setupWrappedHeaders() {
try {
this._headersAll = this._xhr.getAllResponseHeaders();
} catch (e) {}
}
}, {
key: 'addEventListener',

@@ -391,3 +484,3 @@ value: function addEventListener(type, listener, optionsOrUseCapture, wantsUntrusted) {

if (PASSTHROUGH_EVENTS.includes(type)) {
return _get(ThrottledXHR.prototype.__proto__ || Object.getPrototypeOf(ThrottledXHR.prototype), 'addEventListener', this).call(this, type, listener, optionsOrUseCapture, wantsUntrusted);
return _get(XHR.prototype.__proto__ || Object.getPrototypeOf(XHR.prototype), 'addEventListener', this).call(this, type, listener, optionsOrUseCapture, wantsUntrusted);
}

@@ -397,3 +490,3 @@

this._listenersMap.set(listener, listenerWrapper);
return _get(ThrottledXHR.prototype.__proto__ || Object.getPrototypeOf(ThrottledXHR.prototype), 'addEventListener', this).call(this, type, listenerWrapper, optionsOrUseCapture, wantsUntrusted);
return _get(XHR.prototype.__proto__ || Object.getPrototypeOf(XHR.prototype), 'addEventListener', this).call(this, type, listenerWrapper, optionsOrUseCapture, wantsUntrusted);
}

@@ -405,3 +498,3 @@ }, {

if (PASSTHROUGH_EVENTS.includes(type)) {
return _get(ThrottledXHR.prototype.__proto__ || Object.getPrototypeOf(ThrottledXHR.prototype), 'removeEventListener', this).call(this, type, listener, optionsOrUseCapture);
return _get(XHR.prototype.__proto__ || Object.getPrototypeOf(XHR.prototype), 'removeEventListener', this).call(this, type, listener, optionsOrUseCapture);
}

@@ -414,10 +507,70 @@

this._listenersMap.delete(listener);
return _get(ThrottledXHR.prototype.__proto__ || Object.getPrototypeOf(ThrottledXHR.prototype), 'removeEventListener', this).call(this, type, listenerWrapper, optionsOrUseCapture);
return _get(XHR.prototype.__proto__ || Object.getPrototypeOf(XHR.prototype), 'removeEventListener', this).call(this, type, listenerWrapper, optionsOrUseCapture);
}
}, {
key: 'dispatchEvent',
value: function dispatchEvent(event) {
return _get(ThrottledXHR.prototype.__proto__ || Object.getPrototypeOf(ThrottledXHR.prototype), 'dispatchEvent', this).call(this, event);
key: 'open',
value: function open(method, url, async, user, password) {
if (this._caching) {
if (!this._cacheInstance) {
throw new Error('no cache setup');
}
DEBUG && console.log('CACHE GET:', url);
var cachedResource = this._cacheInstance.get(url, false);
if (cachedResource) {
DEBUG && console.log('CACHE HIT');
this._handleCacheHitOnSend(cachedResource);
}
}
return _get(XHR.prototype.__proto__ || Object.getPrototypeOf(XHR.prototype), 'open', this).call(this, method, url, async, user, password);
}
}, {
key: 'send',
value: function send(data) {
if (this._caching && this._cacheHit && this._onSend) {
return this._onSend();
}
return _get(XHR.prototype.__proto__ || Object.getPrototypeOf(XHR.prototype), 'send', this).call(this, data);
}
}, {
key: 'getAllResponseHeaders',
value: function getAllResponseHeaders() {
return this._headersAll;
}
}, {
key: 'caching',
get: function get() {
return this._caching;
},
set: function set(enabled) {
this._caching = enabled;
}
}, {
key: 'cacheInstance',
set: function set(instance) {
this._cacheInstance = instance;
},
get: function get() {
return this._cacheInstance;
}
}, {
key: 'isCacheHit',
get: function get() {
return this._cacheHit;
}
}, {
key: 'cacheWriteEnabled',
set: function set(enable) {
this._cacheWrite = enable;
}
}, {
key: 'enableCacheWrite',
get: function get() {
return this._cacheWrite;
}
}, {
key: 'shaper',

@@ -428,2 +581,9 @@ get: function get() {

}, {
key: 'readyState',
get: function get() {
if (typeof this._readyState === 'number') {
return this._readyState;
}
}
}, {
key: 'onloadend',

@@ -460,8 +620,23 @@ set: function set(fn) {

}
}, {
key: 'response',
get: function get() {
return this._response;
}
}, {
key: 'responseText',
get: function get() {
return this._responseText;
}
}, {
key: 'responseXML',
get: function get() {
return this._responseXML;
}
}]);
return ThrottledXHR;
}(_xhr2.default);
return XHR;
}(_xhrProxy2.default);
exports.default = ThrottledXHR;
exports.default = XHR;

@@ -475,9 +650,9 @@ /***/ }),

var _xhr = __webpack_require__(0);
var _xhrProxy = __webpack_require__(0);
var _xhr2 = _interopRequireDefault(_xhr);
var _xhrProxy2 = _interopRequireDefault(_xhrProxy);
var _throttledXhr = __webpack_require__(1);
var _xhr = __webpack_require__(1);
var _throttledXhr2 = _interopRequireDefault(_throttledXhr);
var _xhr2 = _interopRequireDefault(_xhr);

@@ -490,9 +665,8 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

// Overload native window constructor
global.XMLHttpRequest = _throttledXhr2.default;
global.XMLHttpRequest = _xhr2.default;
}
module.exports = {
XMLHttpRequest: _throttledXhr2.default,
ThrottledXHR: _throttledXhr2.default,
XHRProxy: _xhr2.default,
XMLHttpRequest: _xhr2.default,
XHRProxy: _xhrProxy2.default,
useGlobal: useGlobal

@@ -509,4 +683,169 @@ };

Object.defineProperty(exports, "__esModule", {
value: true
});
var DEFAULT_ALLOW_UPDATES = false;
var MAX_CACHE_SIZE_BYTES = 1024 * 1e6; // 1024 Mbytes
var cache = new Map();
var bytesRead = 0;
var bytesWritten = 0;
var misses = 0;
var hits = 0;
var instance = {
allowUpdates: DEFAULT_ALLOW_UPDATES,
errorOnOverflow: false,
get: function get(uri) {
var onlyData = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var resource = void 0;
if (!cache.has(uri)) {
misses++;
return null;
}
hits++;
resource = cache.get(uri);
resource.accessedAt = Date.now();
if (typeof resource.data.byteLength === 'number') {
bytesRead += resource.data.byteLength;
}
if (onlyData) {
return resource.data;
} else {
return resource;
}
},
put: function put(uri, data, dataText, dataXML, headers, headersAll) {
if (!instance.allowUpdates && cache.has(uri)) {
throw new Error('Cache updates not allowed. Purge first! URI:', uri);
}
var createdAt = Date.now();
var accessedAt = null;
var resource = {
uri: uri,
data: data,
dataText: dataText,
dataXML: dataXML,
headers: headers,
headersAll: headersAll,
createdAt: createdAt,
accessedAt: accessedAt
};
cache.set(uri, resource);
if (typeof resource.data.byteLength === 'number') {
bytesWritten += resource.data.byteLength;
}
var totalSize = instance.countBytes();
if (totalSize > MAX_CACHE_SIZE_BYTES) {
if (instance.errorOnOverflow) throw new Error('Cache exceeds max size, has', totalSize, 'bytes');
instance.purgeOldest();
}
return instance;
},
purgeByUri: function purgeByUri(uri) {
return cache.delete(uri);
},
purgeAll: function purgeAll() {
cache.clear();
},
purgeNotAccessedSince: function purgeNotAccessedSince(timeMillisSince) {
var now = Date.now();
cache.forEach(function (resource, uri) {
if (!resource.accessedAt // never accessed
|| resource.accessedAt < now - timeMillisSince) cache.delete(uri);
});
},
purgeCreatedBefore: function purgeCreatedBefore(timestamp) {
cache.forEach(function (resource, uri) {
if (createdAt < timestamp) cache.delete(uri);
});
},
purgeOldest: function purgeOldest() {
var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'accessed';
var count = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
var prop = type + 'At';
var _loop = function _loop(i) {
var oldest = null;
cache.forEach(function (resource) {
if (!oldest || resource[prop] < oldest[prop]) {
oldest = resource;
}
});
cache.delete(oldest.uri);
};
for (var i = 0; i < count; i++) {
_loop(i);
}
},
reduce: function reduce(reduceFn) {
var accuInit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
var accu = accuInit;
cache.forEach(function (resource, uri) {
accu = reduceFn.bind(undefined)(accu, resource);
});
return accu;
},
sumDataProperty: function sumDataProperty(field) {
return instance.reduce(function (accu, resource) {
return accu + resource.data[field];
});
},
countBytes: function countBytes() {
return instance.sumDataProperty('byteLength');
}
};
var getInfo = function getInfo() {
return {
bytesRead: bytesRead,
bytesWritten: bytesWritten,
hits: hits,
misses: misses
};
};
exports.default = {
getInfo: getInfo,
instance: instance
};
/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function setupCachedXhr(xhr, xhrProxy, cacheInstance) {
xhrProxy._cacheInstance = cacheInstance;
}
exports.default = setupCachedXhr;
/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var XHRReadyStates = {
UNSENT: 0,
OPENED: 1,
HEADERS_RECEIVED: 2,
LOADING: 3,
DONE: 4
};
function setupThrottledXhr(xhr, xhrProxy) {

@@ -520,10 +859,10 @@ var shaper = xhrProxy.shaper;

doneTs = void 0;
var loaded = 0,
total = void 0;
var loaded = 0;
var total = 0;
var currentBitrateKpbs = void 0;
var progressEvents = [];
var progressTimer = void 0;
var progressTimer = null;
var lastProgressEvent = false;
var loadEndEvent = void 0;
var loadEvent = void 0;
var loadEndEvent = null;
var loadEvent = null;
var done = false;

@@ -534,3 +873,2 @@

//console.log('native loadend');

@@ -550,3 +888,4 @@ loadEndEvent = event;

loadEvent = event;
if (done && xhr.readyState === 4) {
if (done && xhr.readyState === XHRReadyStates.DONE) {
xhrProxy._setupWrappedResponseData();
_onload && _onload(event);

@@ -558,2 +897,3 @@ xhrProxy._dispatchWrappedEventType('load');

xhr.onreadystatechange = function (event) {
var now = Date.now();
var _onreadystatechange = xhrProxy._onreadystatechange,

@@ -564,34 +904,41 @@ _onprogress = xhrProxy._onprogress,

var triggerStateChange = function triggerStateChange(e, readyState) {
if (typeof readyState !== 'number') {
throw new Error('readyState should be a number');
}
function triggerStateChange(e) {
xhrProxy._readyState = readyState;
_onreadystatechange && _onreadystatechange(e);
xhrProxy._dispatchWrappedEventType('readystatechange');
}
};
var latency = void 0;
var delay1 = 0;
var delay2 = 0;
switch (xhr.readyState) {
case 0:
// UNSENT
triggerStateChange(event);
triggerStateChange(event, XHRReadyStates.UNSENT);
break;
case 1:
// OPENED
openedTs = Date.now();
triggerStateChange(event);
openedTs = now;
triggerStateChange(event, XHRReadyStates.OPENED);
break;
case 2:
// HEADERS_RECEIVE
headersTs = Date.now();
triggerStateChange(event);
// HEADERS_RECEIVED
headersTs = now;
xhrProxy._setupWrappedHeaders();
triggerStateChange(event, XHRReadyStates.HEADERS_RECEIVED);
break;
case 3:
// LOADING
loadingTs = Date.now();
triggerStateChange(event);
loadingTs = now;
triggerStateChange(event, XHRReadyStates.LOADING);
break;
case 4:
// DONE
var delay1 = 0,
delay2 = 0;
doneTs = Date.now();
var latency = doneTs - openedTs;
doneTs = now;
latency = doneTs - openedTs;
if (latency < shaper.minLatency) {

@@ -612,3 +959,3 @@ delay1 = shaper.minLatency - latency;

triggerStateChange(event);
triggerStateChange(event, XHRReadyStates.DONE);

@@ -618,2 +965,3 @@ done = true;

if (loadEvent) {
xhrProxy._setupWrappedResponseData();
_onload && _onload(loadEvent);

@@ -633,3 +981,4 @@ xhrProxy._dispatchWrappedEventType('load');

done = true;
triggerStateChange(event);
xhrProxy._setupWrappedResponseData();
triggerStateChange(event, XHRReadyStates.DONE);
}

@@ -641,7 +990,6 @@ break;

xhr.onprogress = function (event) {
var now = Date.now();
var _onprogress = xhrProxy._onprogress;
function triggerProgress(e) {
var triggerProgress = function triggerProgress(e) {
if (loaded === total) {

@@ -653,5 +1001,4 @@ lastProgressEvent = true;

xhrProxy._dispatchWrappedEventType('progress');
}
};
var now = Date.now();
var duration = now - openedTs;

@@ -664,8 +1011,5 @@ var delay = void 0;

// console.log('current bitrate: ' + Math.round(currentBitrateKpbs) + ' kbps');
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 () {

@@ -679,7 +1023,2 @@ triggerProgress(event);

};
var id = Math.round(Math.random() * 1e6);
xhr.__throttledId = id;
xhrProxy.__throttledId = id;
}

@@ -690,3 +1029,3 @@

/***/ }),
/* 4 */
/* 6 */
/***/ (function(module, exports, __webpack_require__) {

@@ -693,0 +1032,0 @@

@@ -1,3 +0,3 @@

import XHRProxy from './src/xhr';
import ThrottledXHR from './src/throttled-xhr';
import XHRProxy from './src/xhr-proxy';
import XHR from './src/xhr';

@@ -8,10 +8,9 @@ function useGlobal() {

// Overload native window constructor
global.XMLHttpRequest = ThrottledXHR;
global.XMLHttpRequest = XHR;
}
module.exports = {
XMLHttpRequest: ThrottledXHR,
ThrottledXHR,
XMLHttpRequest: XHR,
XHRProxy,
useGlobal
};
{
"name": "xhr-shaper",
"version": "2.2.1",
"version": "2.2.2",
"description": "Shapes your XHR requests to a max emulated bandwidth and latency, randomizes frequency of progress events",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -0,1 +1,9 @@

var XHRReadyStates = {
UNSENT: 0,
OPENED: 1,
HEADERS_RECEIVED: 2,
LOADING: 3,
DONE: 4
};
function setupThrottledXhr(xhr, xhrProxy) {

@@ -8,9 +16,10 @@

let openedTs, headersTs, loadingTs, doneTs;
let loaded = 0, total;
let loaded = 0;
let total = 0;
let currentBitrateKpbs;
let progressEvents = [];
let progressTimer;
let progressTimer = null;
let lastProgressEvent = false;
let loadEndEvent;
let loadEvent;
let loadEndEvent = null;
let loadEvent = null;
let done = false;

@@ -24,3 +33,2 @@

//console.log('native loadend');
loadEndEvent = event;

@@ -41,3 +49,4 @@ if (done) {

loadEvent = event;
if (done && xhr.readyState === 4) {
if (done && xhr.readyState === XHRReadyStates.DONE) {
xhrProxy._setupWrappedResponseData();
_onload && _onload(event);

@@ -49,4 +58,4 @@ xhrProxy._dispatchWrappedEventType('load');

xhr.onreadystatechange = function(event) {
let {
const now = Date.now();
const {
_onreadystatechange,

@@ -57,4 +66,8 @@ _onprogress,

} = xhrProxy;
const triggerStateChange = function(e, readyState) {
if (typeof readyState !== 'number') {
throw new Error('readyState should be a number');
}
function triggerStateChange(e) {
xhrProxy._readyState = readyState;
_onreadystatechange && _onreadystatechange(e);

@@ -64,22 +77,26 @@ xhrProxy._dispatchWrappedEventType('readystatechange');

let latency;
let delay1 = 0;
let delay2 = 0;
switch (xhr.readyState) {
case 0: // UNSENT
triggerStateChange(event);
triggerStateChange(event, XHRReadyStates.UNSENT);
break;
case 1: // OPENED
openedTs = Date.now();
triggerStateChange(event);
openedTs = now;
triggerStateChange(event, XHRReadyStates.OPENED);
break;
case 2: // HEADERS_RECEIVE
headersTs = Date.now();
triggerStateChange(event);
case 2: // HEADERS_RECEIVED
headersTs = now;
xhrProxy._setupWrappedHeaders();
triggerStateChange(event, XHRReadyStates.HEADERS_RECEIVED);
break;
case 3: // LOADING
loadingTs = Date.now();
triggerStateChange(event);
loadingTs = now;
triggerStateChange(event, XHRReadyStates.LOADING);
break;
case 4: // DONE
let delay1 = 0, delay2 = 0;
doneTs = Date.now();
let latency = doneTs - openedTs;
doneTs = now;
latency = doneTs - openedTs;
if (latency < shaper.minLatency) {

@@ -100,3 +117,3 @@ delay1 = shaper.minLatency - latency;

triggerStateChange(event);
triggerStateChange(event, XHRReadyStates.DONE);

@@ -106,2 +123,3 @@ done = true;

if (loadEvent) {
xhrProxy._setupWrappedResponseData();
_onload && _onload(loadEvent);

@@ -122,3 +140,4 @@ xhrProxy._dispatchWrappedEventType('load');

done = true;
triggerStateChange(event);
xhrProxy._setupWrappedResponseData();
triggerStateChange(event, XHRReadyStates.DONE);
}

@@ -130,9 +149,7 @@ break;

xhr.onprogress = function(event) {
let {
const now = Date.now();
const {
_onprogress
} = xhrProxy;
function triggerProgress(e) {
const triggerProgress = function(e) {
if (loaded === total) {

@@ -146,3 +163,2 @@ lastProgressEvent = true;

let now = Date.now();
let duration = now - openedTs;

@@ -155,8 +171,5 @@ let delay;

// console.log('current bitrate: ' + Math.round(currentBitrateKpbs) + ' kbps');
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() {

@@ -170,9 +183,4 @@ triggerProgress(event);

};
let id = Math.round(Math.random() * 1e6);
xhr.__throttledId = id;
xhrProxy.__throttledId = id;
}
export default setupThrottledXhr;

@@ -1,152 +0,265 @@

// See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
import XHRProxy from './xhr-proxy';
import Shaper from './shaper';
import setupThrottledXhr from './setup-throttled-xhr';
import setupCachedXhr from './setup-cached-xhr';
import cache from './cache';
const XMLHttpRequest = window.XMLHttpRequest;
const DEBUG = false;
class XHRProxy {
const PASSTHROUGH_EVENTS = ['loadstart', 'timeout', 'abort', 'error'];
constructor() {
this._xhr = new XMLHttpRequest();
const createListenerWrapper = (type, listener, dispatchedEventsList) => {
return (event) => {
dispatchedEventsList.push({type, listener, event, propagated: false});
};
}
class XHR extends XHRProxy {
static get Shaper() {
return Shaper;
}
// methods
static get cache() {
return cache;
}
abort() {
return this._xhr.abort();
constructor() {
super();
this._cacheInstance = cache.instance;
this._shaper = new Shaper();
this._listenersMap = new Map();
this._dispatchedEventsList = [];
this._response = null;
this._responseText = null;
this._responseXML = null;
this._headers = null;
this._readyState = 0;
this._caching = false;
this._cacheHit = false;
this._cacheWrite = true;
this._onSend = null;
this.addEventListener('loadend', () => {
if (this._caching && this._cacheWrite) {
DEBUG && console.log('CACHE WRITE:', this._xhr.responseURL);
this._cacheInstance.put(
this._xhr.responseURL,
this._response,
this._responseText,
this._responseXML,
null,
this._xhr.getAllResponseHeaders()
);
}
});
setupThrottledXhr(this._xhr, this);
}
open(method, url, async, user, password) {
return this._xhr.open(method, url, async, user, password);
get caching() {
return this._caching;
}
send(data) {
return this._xhr.send(data);
set caching(enabled) {
this._caching = enabled;
}
setRequestHeader(header, value) {
return this._xhr.setRequestHeader(header, value);
set cacheInstance(instance) {
this._cacheInstance = instance;
}
getResponseHeader(header) {
return this._xhr.getResponseHeader(header);
get cacheInstance() {
return this._cacheInstance;
}
overrideMimeType(mimeType) {
return this._xhr.overrideMimeType(mimeType);
get isCacheHit() {
return this._cacheHit;
}
getAllResponseHeaders() {
return this._xhr.getAllResponseHeaders();
set cacheWriteEnabled(enable) {
this._cacheWrite = enable;
}
addEventListener(type, listener, optionsOrUseCapture, wantsUntrusted) {
return this._xhr.addEventListener(type, listener, optionsOrUseCapture, wantsUntrusted);
get enableCacheWrite() {
return this._cacheWrite;
}
removeEventListener(type, listener, optionsOrUseCapture) {
return this._xhr.removeEventListener(type, listener, optionsOrUseCapture);
get shaper() {
return this._shaper;
}
dispatchEvent(event) {
return this._xhr.dispatchEvent(event);
_dispatchWrappedEventType(type) {
// it needs to run on the next tick since this is actually
// triggered from our throttler listeners on the proxy's inner XHR
setTimeout(() => {
this._dispatchedEventsList
.filter((dispatchedEvent) => (dispatchedEvent.type === type && !dispatchedEvent.propagated))
.forEach((dispatchedEvent) => {
dispatchedEvent.propagated = true;
dispatchedEvent.listener(dispatchedEvent.event);
});
}, 0);
}
// Read-only properties
_handleCacheHitOnSend(cachedResource) {
this._readyState = 1;
this._cacheHit = true;
get readyState() {
return this._xhr.readyState;
const onSend = () => {
setTimeout(() => {
this._readyState = 2;
this.onreadystatechange && this.onreadystatechange();
this._dispatchWrappedEventType('readystatechange');
this._readyState = 3;
this.onreadystatechange && this.onreadystatechange();
this._dispatchWrappedEventType('readystatechange');
this.onloadstart && this.onloadstart();
this._dispatchWrappedEventType('loadstart');
this._response = cachedResource.data;
this._responseText = cachedResource.dataText;
this._responseXML = cachedResource.dataXML;
this._headersAll = cachedResource.headers;
this._readyState = 4;
this.onreadystatechange && this.onreadystatechange();
this._dispatchWrappedEventType('readystatechange');
this.onreadystatechange && this.onreadystatechange();
this._dispatchWrappedEventType('readystatechange');
this.onload && this.onload();
this._dispatchWrappedEventType('load');
this.onloadend && this.onloadend();
this._dispatchWrappedEventType('loadend');
}, 0);
};
this._onSend = onSend;
}
get response() {
return this._xhr.response;
_setupWrappedResponseData() {
try {
this._response = this._xhr.response;
} catch(e) {
DEBUG && console.warn(e)
}
try {
this._responseText = this._xhr.responseText;
} catch(e) {
DEBUG && console.warn(e)
}
try {
this._responseXML = this._xhr.responseXML;
} catch(e) {
DEBUG && console.warn(e)
}
}
get responseText() {
return this._xhr.responseText;
_setupWrappedHeaders() {
try { this._headersAll = this._xhr.getAllResponseHeaders(); } catch(e) {}
}
get responseXML() {
return this._xhr.responseXML;
addEventListener(type, listener, optionsOrUseCapture, wantsUntrusted) {
if (PASSTHROUGH_EVENTS.includes(type)) {
return super.addEventListener(type, listener, optionsOrUseCapture, wantsUntrusted);
}
const listenerWrapper = createListenerWrapper(type, listener, this._dispatchedEventsList);
this._listenersMap.set(listener, listenerWrapper);
return super.addEventListener(type, listenerWrapper, optionsOrUseCapture, wantsUntrusted);
}
get responseURL() {
return this._xhr.responseURL;
}
get status() {
return this._xhr.status;
}
get statusText() {
return this._xhr.statusText;
}
get upload() {
return this._xhr.upload;
}
// R & W properties
set withCredentials(enabled) {
this._xhr.withCredentials = enabled;
removeEventListener(type, listener, optionsOrUseCapture) {
if (PASSTHROUGH_EVENTS.includes(type)) {
return super.removeEventListener(type, listener, optionsOrUseCapture)
}
const listenerWrapper = this._listenersMap.get(listener);
if (!listenerWrapper) {
return;
}
this._listenersMap.delete(listener);
return super.removeEventListener(type, listenerWrapper, optionsOrUseCapture);
}
get withCredentials() {
return this._xhr.withCredentials;
}
set responseType(responseType) {
this._xhr.responseType = responseType;
open(method, url, async, user, password) {
if (this._caching) {
if (!this._cacheInstance) {
throw new Error('no cache setup');
}
DEBUG && console.log('CACHE GET:', url);
const cachedResource = this._cacheInstance.get(url, false);
if (cachedResource) {
DEBUG && console.log('CACHE HIT');
this._handleCacheHitOnSend(cachedResource);
}
}
return super.open(method, url, async, user, password);
}
get responseType() {
return this._xhr.responseType;
}
set timeout(timeout) {
this._xhr.timeout = timeout;
send(data) {
if (this._caching && this._cacheHit && this._onSend) {
return this._onSend();
}
return super.send(data);
}
get timeout() {
return this._xhr.timeout;
}
set onload(fn) {
this._xhr.onload = fn;
getAllResponseHeaders() {
return this._headersAll;
}
get onload() {
return this._xhr.onload;
}
set onloadstart(fn) {
this._xhr.onloadstart = fn;
get readyState() {
if (typeof this._readyState === 'number') {
return this._readyState;
}
}
get onloadstart() {
return this._xhr.onloadstart;
}
set onloadend(fn) {
this._xhr.onloadend = fn;
this._onloadend = fn;
}
get onloadend() {
return this._xhr.onloadend;
return this._onloadend;
}
set onabort(fn) {
this._xhr.onabort = fn;
set onload(fn) {
this._onload = fn;
}
get onabort() {
return this._xhr.onabort;
get onload() {
return this._onload;
}
set onerror(fn) {
this._xhr.onerror = fn;
set onreadystatechange(fn) {
this._onreadystatechange = fn;
}
get onerror() {
return this._xhr.onerror;
get onreadystatechange() {
return this._onreadystatechange;
}
set onprogress(fn) {
this._xhr.onprogress = fn;
this._onprogress = fn;
}
get onprogress() {
return this._xhr.onprogress;
return this._onprogress;
}
set ontimeout(fn) {
this._xhr.ontimeout = fn;
get response() {
return this._response;
}
get ontimeout() {
return this._xhr.ontimeout;
get responseText() {
return this._responseText;
}
set onreadystatechange(fn) {
this._xhr.onreadystatechange = fn;
get responseXML() {
return this._responseXML;
}
get onreadystatechange() {
return this._xhr.onreadystatechange;
}
}
export default XHRProxy;
export default XHR;

@@ -38,2 +38,6 @@ ///////////////////////////////////

it('should make a large throttled request and accurately limit the bandwidth', function(done) {
var xhr = makeRequest(bigChunkUrl, done, 0, 512);
});
it('should make two consequent requests', function(done) {

@@ -40,0 +44,0 @@ var xhr = makeRequest(fooUrl, function() {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc