lifen-postman/node_modules/uvm/lib/bridge.browser.js
Simon Priet e69a613a37 feat: Created a mini nodeJS server with NewMan for testing without PostMan GUI.
This will mimic a run in a CD/CI environment or docker container.
2021-09-08 14:01:19 +02:00

155 lines
5.4 KiB
JavaScript

/* istanbul ignore file */
/*
* @note options.dispatchTimeout is not implemented in browser sandbox because
* there is no way to interrupt an infinite loop.
* Maybe terminate and restart the worker or execute in nested worker.
*/
const Flatted = require('flatted'),
{ randomNumber } = require('./utils'),
ERROR = 'error',
MESSAGE = 'message',
UVM_ID_ = '__id_uvm_',
// code for bridge
bridgeClientCode = require('./bridge-client'),
/**
* Returns the firmware code to be executed inside Web Worker.
*
* @private
* @param {String} code -
* @param {String} id -
* @return {String}
*/
sandboxFirmware = (code, id) => {
// @note self.postMessage and self.addEventListener methods are cached
// in variable or closure because bootCode might mutate the global scope
return `
__uvm_emit = function (postMessage, args) {
postMessage({__id_uvm: "${id}",__emit_uvm: args});
}.bind(null, self.postMessage);
__uvm_addEventListener = self.addEventListener;
try {${code}} catch (e) { setTimeout(function () { throw e; }, 0); }
(function (emit, id) {
__uvm_addEventListener("message", function (e) {
(e && e.data && (typeof e.data.__emit_uvm === 'string') && (e.data.__id_uvm === id)) &&
emit(e.data.__emit_uvm);
});
}(__uvm_dispatch, "${id}"));
__uvm_emit('${Flatted.stringify(['load.' + id])}');
__uvm_dispatch = null; __uvm_emit = null; __uvm_addEventListener = null;
delete __uvm_dispatch; delete __uvm_emit; delete __uvm_addEventListener;
`;
};
module.exports = function (bridge, options, callback) {
if (!(Blob && Worker && window && window.URL && window.URL.createObjectURL)) {
return callback(new Error('uvm: unable to setup communication bridge, missing required APIs'));
}
let worker,
bootTimer,
firmwareCode,
firmwareObjectURL;
const id = UVM_ID_ + randomNumber(),
// function to forward messages emitted
forwardEmits = (e) => {
if (!(e && e.data && (typeof e.data.__emit_uvm === 'string') && (e.data.__id_uvm === id))) { return; }
let args;
try { args = Flatted.parse(e.data.__emit_uvm); }
catch (err) { return bridge.emit(ERROR, err); }
bridge.emit(...args);
},
// function to forward errors emitted
forwardErrors = (e) => {
bridge.emit(ERROR, e);
},
// function to terminate worker
terminateWorker = function () {
if (!worker) { return; }
// remove event listeners for this sandbox
worker.removeEventListener(MESSAGE, forwardEmits);
worker.removeEventListener(ERROR, forwardErrors);
// do not terminate sandbox worker if not spawned for the bridge
if (!options._sandbox) {
worker.terminate();
// revoke after termination. otherwise, blob reference is retained until GC
// refer: "chrome://blob-internals"
window.URL.revokeObjectURL(firmwareObjectURL);
}
worker = null;
};
// on load attach the dispatcher
bridge.once('load.' + id, () => {
// stop boot timer first
clearTimeout(bootTimer);
bridge._dispatch = function () {
if (!worker) {
return bridge.emit(ERROR,
new Error('uvm: unable to dispatch "' + arguments[0] + '" post disconnection.'));
}
worker.postMessage({
__emit_uvm: Flatted.stringify(Array.prototype.slice.call(arguments)),
__id_uvm: id
});
};
callback(null, bridge);
});
// get firmware code string with boot code
firmwareCode = sandboxFirmware(bridgeClientCode(options.bootCode), id);
// start boot timer, stops once we get the load signal, terminate otherwise
bootTimer = setTimeout(() => {
terminateWorker();
callback(new Error(`uvm: boot timed out after ${options.bootTimeout}ms.`));
}, options.bootTimeout);
// if sandbox worker is provided, we simply need to init with firmware code
// @todo validate sandbox type or APIs
if (options._sandbox) {
worker = options._sandbox;
worker.postMessage({ __init_uvm: firmwareCode });
}
// else, spawn a new worker
else {
// convert the firmware code into a blob URL
firmwareObjectURL = window.URL.createObjectURL(new Blob([firmwareCode], { type: 'text/javascript' }));
// catch CSP:worker-src violations
try { worker = new Worker(firmwareObjectURL); }
catch (error) {
// clear blob reference
window.URL.revokeObjectURL(firmwareObjectURL);
return callback(new Error(`uvm: unable to spawn worker.\n${error.message || error}`));
}
}
// add event listener for receiving events from worker (is removed on disconnect)
// don't set `onmessage` and `onerror` as it might override external sandbox
worker.addEventListener(MESSAGE, forwardEmits);
worker.addEventListener(ERROR, forwardErrors);
// equip bridge to disconnect (i.e. terminate the worker)
bridge._disconnect = terminateWorker;
// help GC collect large variables
firmwareCode = null;
};