Throw in openpgp.initWorker if worker failed to load
This commit is contained in:
parent
29f29f6c6e
commit
9394fec1f4
|
@ -76,14 +76,12 @@ let asyncProxy; // instance of the asyncproxy
|
|||
*/
|
||||
export async function initWorker({ path = 'openpgp.worker.js', n = 1, workers = [] } = {}) {
|
||||
if (workers.length || (typeof global !== 'undefined' && global.Worker && global.MessageChannel)) {
|
||||
const proxy = new AsyncProxy({ path, n, workers, config });
|
||||
const loaded = await proxy.loaded();
|
||||
if (loaded) {
|
||||
asyncProxy = proxy;
|
||||
return true;
|
||||
}
|
||||
const proxy = new AsyncProxy();
|
||||
await proxy.init({ path, n, workers, config });
|
||||
asyncProxy = proxy;
|
||||
} else {
|
||||
throw new Error('Web Workers are not available');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -35,23 +35,25 @@ import packet from '../packet';
|
|||
|
||||
|
||||
/**
|
||||
* Initializes a new proxy and loads the web worker
|
||||
* Creates a new async proxy
|
||||
* @constructor
|
||||
*/
|
||||
function AsyncProxy() {}
|
||||
|
||||
/**
|
||||
* Initializes the proxy and loads the web worker
|
||||
* @param {String} path The path to the worker or 'openpgp.worker.js' by default
|
||||
* @param {Number} n number of workers to initialize if path given
|
||||
* @param {Object} config config The worker configuration
|
||||
* @param {Array<Object>} worker alternative to path parameter: web worker initialized with 'openpgp.worker.js'
|
||||
* @constructor
|
||||
*/
|
||||
function AsyncProxy({ path = 'openpgp.worker.js', n = 1, workers = [], config } = {}) {
|
||||
AsyncProxy.prototype.init = async function({ path = 'openpgp.worker.js', n = 1, workers = [], config } = {}) {
|
||||
/**
|
||||
* Message handling
|
||||
*/
|
||||
const handleMessage = workerId => event => {
|
||||
const msg = event.data;
|
||||
switch (msg.event) {
|
||||
case 'loaded':
|
||||
this.workers[workerId].loadedResolve(true);
|
||||
break;
|
||||
case 'method-return':
|
||||
if (msg.err) {
|
||||
// fail
|
||||
|
@ -84,37 +86,21 @@ function AsyncProxy({ path = 'openpgp.worker.js', n = 1, workers = [], config }
|
|||
}
|
||||
}
|
||||
|
||||
let workerId = 0;
|
||||
this.workers.forEach(worker => {
|
||||
worker.loadedPromise = new Promise(resolve => {
|
||||
worker.loadedResolve = resolve;
|
||||
});
|
||||
worker.requests = 0;
|
||||
worker.onmessage = handleMessage(workerId++);
|
||||
worker.onerror = e => {
|
||||
worker.loadedResolve(false);
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Unhandled error in openpgp worker: ' + e.message + ' (' + e.filename + ':' + e.lineno + ')');
|
||||
return false;
|
||||
};
|
||||
|
||||
if (config) {
|
||||
worker.postMessage({ event:'configure', config });
|
||||
}
|
||||
});
|
||||
|
||||
// Cannot rely on task order being maintained, use object keyed by request ID to track tasks
|
||||
this.tasks = {};
|
||||
this.currentID = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that resolves when all workers have finished loading
|
||||
* @returns {Promise<Boolean>} Resolves to true if all workers have loaded succesfully; false otherwise
|
||||
*/
|
||||
AsyncProxy.prototype.loaded = async function() {
|
||||
const loaded = await Promise.all(this.workers.map(worker => worker.loadedPromise));
|
||||
return loaded.every(Boolean);
|
||||
let workerId = 0;
|
||||
await Promise.all(this.workers.map(worker => new Promise(async (resolve, reject) => {
|
||||
worker.requests = 0;
|
||||
worker.onmessage = handleMessage(workerId++);
|
||||
worker.onerror = e => {
|
||||
reject(new Error('Unhandled error in openpgp worker: ' + e.message + ' (' + e.filename + ':' + e.lineno + ')'));
|
||||
};
|
||||
|
||||
await this.callWorker(worker, 'configure', config);
|
||||
resolve();
|
||||
})));
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -132,7 +118,7 @@ AsyncProxy.prototype.getID = function() {
|
|||
*/
|
||||
AsyncProxy.prototype.seedRandom = async function(workerId, size) {
|
||||
const buf = await crypto.random.getRandomBytes(size);
|
||||
this.workers[workerId].postMessage({ event:'seed-random', buf }, util.getTransferables(buf, true));
|
||||
this.workers[workerId].postMessage({ event: 'seed-random', buf }, util.getTransferables(buf, true));
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -140,13 +126,7 @@ AsyncProxy.prototype.seedRandom = async function(workerId, size) {
|
|||
* @async
|
||||
*/
|
||||
AsyncProxy.prototype.clearKeyCache = async function() {
|
||||
await Promise.all(this.workers.map(worker => new Promise((resolve, reject) => {
|
||||
const id = this.getID();
|
||||
|
||||
worker.postMessage({ id, event: 'clear-key-cache' });
|
||||
|
||||
this.tasks[id] = { resolve, reject };
|
||||
})));
|
||||
await Promise.all(this.workers.map(worker => this.callWorker(worker, 'clear-key-cache')));
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -165,9 +145,7 @@ AsyncProxy.prototype.terminate = function() {
|
|||
* @returns {Promise} see the corresponding public api functions for their return types
|
||||
* @async
|
||||
*/
|
||||
AsyncProxy.prototype.delegate = function(method, options) {
|
||||
|
||||
const id = this.getID();
|
||||
AsyncProxy.prototype.delegate = async function (method, options) {
|
||||
const requests = this.workers.map(worker => worker.requests);
|
||||
const minRequests = Math.min(...requests);
|
||||
let workerId = 0;
|
||||
|
@ -177,15 +155,21 @@ AsyncProxy.prototype.delegate = function(method, options) {
|
|||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const data = { id, event: method, options: packet.clone.clonePackets(options) };
|
||||
const transferables = util.getTransferables(data, config.zero_copy);
|
||||
// clone packets (for web worker structured cloning algorithm)
|
||||
this.workers[workerId].postMessage(data, transferables);
|
||||
this.workers[workerId].requests++;
|
||||
const data = { options: packet.clone.clonePackets(options) };
|
||||
const transferables = util.getTransferables(data, config.zero_copy);
|
||||
const worker = this.workers[workerId];
|
||||
worker.requests++;
|
||||
const result = await this.callWorker(worker, method, data.options, transferables);
|
||||
return packet.clone.parseClonedPackets(util.restoreStreams(result, options.streaming), method);
|
||||
};
|
||||
|
||||
// remember to handle parsing cloned packets from worker
|
||||
this.tasks[id] = { resolve: data => resolve(packet.clone.parseClonedPackets(util.restoreStreams(data, options.streaming), method)), reject };
|
||||
AsyncProxy.prototype.callWorker = function(worker, method, options, transferables) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const id = this.getID();
|
||||
|
||||
worker.postMessage({ id, method, options }, transferables);
|
||||
|
||||
this.tasks[id] = { resolve, reject };
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -58,11 +58,7 @@ openpgp.crypto.random.randomBuffer.init(MAX_SIZE_RANDOM_BUFFER, randomCallback);
|
|||
self.onmessage = function(event) {
|
||||
var msg = event.data || {};
|
||||
|
||||
switch (msg.event) {
|
||||
case 'configure':
|
||||
configure(msg.config);
|
||||
break;
|
||||
|
||||
switch (msg.method) {
|
||||
case 'seed-random':
|
||||
seedRandom(msg.buf);
|
||||
|
||||
|
@ -75,7 +71,7 @@ self.onmessage = function(event) {
|
|||
break;
|
||||
|
||||
default:
|
||||
delegate(msg.id, msg.event, msg.options || {});
|
||||
delegate(msg.id, msg.method, msg.options || {});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -117,6 +113,11 @@ function getCachedKey(key) {
|
|||
* @param {Object} options The api function's options
|
||||
*/
|
||||
function delegate(id, method, options) {
|
||||
if (method === 'configure') {
|
||||
configure(options);
|
||||
response({ id, event: 'method-return' });
|
||||
return;
|
||||
}
|
||||
if (method === 'clear-key-cache') {
|
||||
Array.from(keyCache.values()).forEach(key => {
|
||||
if (key.isPrivate()) {
|
||||
|
@ -161,7 +162,3 @@ function response(event) {
|
|||
self.postMessage(event, openpgp.util.getTransferables(event.data, openpgp.config.zero_copy));
|
||||
}
|
||||
|
||||
/**
|
||||
* Let the main window know the worker has loaded.
|
||||
*/
|
||||
postMessage({ event: 'loaded' });
|
||||
|
|
|
@ -347,7 +347,11 @@ tryTests('Brainpool Omnibus Tests @lightweight', omnibus, {
|
|||
tryTests('Brainpool Omnibus Tests - Worker @lightweight', omnibus, {
|
||||
if: typeof window !== 'undefined' && window.Worker && (openpgp.config.use_indutny_elliptic || openpgp.util.getNodeCrypto()),
|
||||
before: async function() {
|
||||
await openpgp.initWorker({ path: '../dist/openpgp.worker.js' });
|
||||
try {
|
||||
await openpgp.initWorker({ path: '../dist/openpgp.worker.js' });
|
||||
} catch (e) {
|
||||
openpgp.util.print_debug_error(e);
|
||||
}
|
||||
},
|
||||
beforeEach: function() {
|
||||
openpgp.config.use_native = true;
|
||||
|
|
|
@ -101,7 +101,11 @@ describe('Elliptic Curve Cryptography for NIST P-256,P-384,P-521 curves @lightwe
|
|||
tryTests('ECC Worker Tests', omnibus, {
|
||||
if: typeof window !== 'undefined' && window.Worker,
|
||||
before: async function() {
|
||||
await openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
|
||||
try {
|
||||
await openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
|
||||
} catch (e) {
|
||||
openpgp.util.print_debug_error(e);
|
||||
}
|
||||
},
|
||||
beforeEach: function() {
|
||||
openpgp.config.use_native = true;
|
||||
|
|
|
@ -2005,7 +2005,7 @@ function versionSpecificTests() {
|
|||
openpgp.config.aead_mode = openpgp.enums.aead.experimental_gcm;
|
||||
if (openpgp.getWorker()) {
|
||||
openpgp.getWorker().workers.forEach(worker => {
|
||||
worker.postMessage({ event: 'configure', config: openpgp.config });
|
||||
openpgp.getWorker().callWorker(worker, 'configure', openpgp.config);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2041,7 +2041,7 @@ function versionSpecificTests() {
|
|||
openpgp.config.aead_mode = aead_modeVal;
|
||||
if (openpgp.getWorker()) {
|
||||
openpgp.getWorker().workers.forEach(worker => {
|
||||
worker.postMessage({ event: 'configure', config: openpgp.config });
|
||||
openpgp.getWorker().callWorker(worker, 'configure', openpgp.config);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2545,7 +2545,11 @@ describe('Key', function() {
|
|||
tryTests('V4 - With Worker', versionSpecificTests, {
|
||||
if: typeof window !== 'undefined' && window.Worker,
|
||||
before: async function() {
|
||||
await openpgp.initWorker({ path: '../dist/openpgp.worker.js' });
|
||||
try {
|
||||
await openpgp.initWorker({ path: '../dist/openpgp.worker.js' });
|
||||
} catch (e) {
|
||||
openpgp.util.print_debug_error(e);
|
||||
}
|
||||
},
|
||||
after: function() {
|
||||
openpgp.destroyWorker();
|
||||
|
|
|
@ -516,7 +516,7 @@ describe('OpenPGP.js public api tests', function() {
|
|||
openpgp.initWorker({
|
||||
workers: [workerStub]
|
||||
}),
|
||||
workerStub.onmessage({ data: { event: 'loaded' } })
|
||||
workerStub.onmessage({ data: { id: 0, event: 'method-return' } })
|
||||
]);
|
||||
expect(openpgp.getWorker()).to.exist;
|
||||
openpgp.destroyWorker();
|
||||
|
@ -671,7 +671,7 @@ describe('OpenPGP.js public api tests', function() {
|
|||
openpgp.initWorker({
|
||||
workers: [workerStub]
|
||||
}),
|
||||
workerStub.onmessage({ data: { event: 'loaded' } })
|
||||
workerStub.onmessage({ data: { id: 0, event: 'method-return' } })
|
||||
]);
|
||||
const proxyGenStub = stub(openpgp.getWorker(), 'delegate');
|
||||
getWebCryptoAllStub.returns();
|
||||
|
@ -716,7 +716,11 @@ describe('OpenPGP.js public api tests', function() {
|
|||
|
||||
it('should work in JS (with worker)', async function() {
|
||||
openpgp.config.use_native = false;
|
||||
await openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
|
||||
try {
|
||||
await openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
|
||||
} catch (e) {
|
||||
openpgp.util.print_debug_error(e);
|
||||
}
|
||||
const opt = {
|
||||
userIds: [{ name: 'Test User', email: 'text@example.com' }],
|
||||
numBits: 512
|
||||
|
@ -821,7 +825,11 @@ describe('OpenPGP.js public api tests', function() {
|
|||
const { workers } = openpgp.getWorker();
|
||||
try {
|
||||
await privateKey.keys[0].decrypt(passphrase)
|
||||
await openpgp.initWorker({path: '../dist/openpgp.worker.js', workers, n: 2});
|
||||
try {
|
||||
await openpgp.initWorker({path: '../dist/openpgp.worker.js', workers, n: 2});
|
||||
} catch (e) {
|
||||
openpgp.util.print_debug_error(e);
|
||||
}
|
||||
|
||||
const workerTest = (_, index) => {
|
||||
const plaintext = input.createSomeMessage() + index;
|
||||
|
@ -842,7 +850,11 @@ describe('OpenPGP.js public api tests', function() {
|
|||
};
|
||||
await Promise.all(Array(10).fill(null).map(workerTest));
|
||||
} finally {
|
||||
await openpgp.initWorker({path: '../dist/openpgp.worker.js', workers, n: 1 });
|
||||
try {
|
||||
await openpgp.initWorker({path: '../dist/openpgp.worker.js', workers, n: 1 });
|
||||
} catch (e) {
|
||||
openpgp.util.print_debug_error(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -900,7 +912,11 @@ describe('OpenPGP.js public api tests', function() {
|
|||
tryTests('CFB mode (asm.js, worker)', tests, {
|
||||
if: typeof window !== 'undefined' && window.Worker,
|
||||
before: async function() {
|
||||
await openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
|
||||
try {
|
||||
await openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
|
||||
} catch (e) {
|
||||
openpgp.util.print_debug_error(e);
|
||||
}
|
||||
},
|
||||
beforeEach: function() {
|
||||
openpgp.config.aead_protect = false;
|
||||
|
@ -1641,7 +1657,7 @@ describe('OpenPGP.js public api tests', function() {
|
|||
openpgp.config.allow_unauthenticated_stream = !!allow_streaming;
|
||||
if (openpgp.getWorker()) {
|
||||
openpgp.getWorker().workers.forEach(worker => {
|
||||
worker.postMessage({ event: 'configure', config: openpgp.config });
|
||||
openpgp.getWorker().callWorker(worker, 'configure', openpgp.config);
|
||||
});
|
||||
}
|
||||
await Promise.all([badSumEncrypted, badBodyEncrypted].map(async (encrypted, i) => {
|
||||
|
@ -1847,7 +1863,7 @@ describe('OpenPGP.js public api tests', function() {
|
|||
openpgp.config.zero_copy = false;
|
||||
if (openpgp.getWorker()) {
|
||||
openpgp.getWorker().workers.forEach(worker => {
|
||||
worker.postMessage({ event: 'configure', config: openpgp.config });
|
||||
openpgp.getWorker().callWorker(worker, 'configure', openpgp.config);
|
||||
});
|
||||
}
|
||||
return openpgp.decrypt(decOpt);
|
||||
|
|
|
@ -1334,7 +1334,11 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA
|
|||
tryTests('With Worker', tests, {
|
||||
if: typeof window !== 'undefined' && window.Worker,
|
||||
before: async function() {
|
||||
await openpgp.initWorker({ path: '../dist/openpgp.worker.js' });
|
||||
try {
|
||||
await openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
|
||||
} catch (e) {
|
||||
openpgp.util.print_debug_error(e);
|
||||
}
|
||||
},
|
||||
after: function() {
|
||||
openpgp.destroyWorker();
|
||||
|
|
|
@ -545,7 +545,11 @@ tryTests('X25519 Omnibus Tests', omnibus, {
|
|||
tryTests('X25519 Omnibus Tests - Worker', omnibus, {
|
||||
if: typeof window !== 'undefined' && window.Worker,
|
||||
before: async function() {
|
||||
await openpgp.initWorker({ path: '../dist/openpgp.worker.js' });
|
||||
try {
|
||||
await openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
|
||||
} catch (e) {
|
||||
openpgp.util.print_debug_error(e);
|
||||
}
|
||||
},
|
||||
beforeEach: function() {
|
||||
openpgp.config.use_native = true;
|
||||
|
|
|
@ -37,7 +37,11 @@ let pubKey;
|
|||
tryTests('Async Proxy', tests, {
|
||||
if: typeof window !== 'undefined' && window.Worker && window.MessageChannel,
|
||||
before: async function() {
|
||||
await openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
|
||||
try {
|
||||
await openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
|
||||
} catch (e) {
|
||||
openpgp.util.print_debug_error(e);
|
||||
}
|
||||
pubKey = (await openpgp.key.readArmored(pub_key)).keys[0];
|
||||
},
|
||||
after: async function() {
|
||||
|
@ -50,11 +54,14 @@ function tests() {
|
|||
describe('Random number pipeline', function() {
|
||||
it('Random number buffer automatically reseeded', async function() {
|
||||
const worker = new Worker('../dist/openpgp.worker.js');
|
||||
const wProxy = new openpgp.AsyncProxy({ path:'../dist/openpgp.worker.js', workers: [worker] });
|
||||
const loaded = await wProxy.loaded();
|
||||
if (loaded) {
|
||||
return wProxy.delegate('encrypt', { publicKeys:[pubKey], message:openpgp.message.fromText(plaintext) });
|
||||
const wProxy = new openpgp.AsyncProxy();
|
||||
try {
|
||||
await wProxy.init({ path:'../dist/openpgp.worker.js', workers: [worker] });
|
||||
} catch (e) {
|
||||
openpgp.util.print_debug_error(e);
|
||||
return;
|
||||
}
|
||||
return wProxy.delegate('encrypt', { publicKeys:[pubKey], message:openpgp.message.fromText(plaintext) });
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user