Don't attempt to use workers if they fail to load

This commit is contained in:
Daniel Huigens 2019-05-01 22:03:37 +02:00
parent ecc8ae2a09
commit 34e6eacb2f
9 changed files with 69 additions and 32 deletions

View File

@ -119,7 +119,7 @@ Here are some examples of how to use the v2.x+ API. For more elaborate examples
```js
var openpgp = require('openpgp'); // use as CommonJS, AMD, ES6 module or via window.openpgp
openpgp.initWorker({ path:'openpgp.worker.js' }) // set the relative web worker path
await openpgp.initWorker({ path:'openpgp.worker.js' }) // set the relative web worker path
```
#### Encrypt and decrypt *Uint8Array* data with a password
@ -159,7 +159,7 @@ Encryption will use the algorithm preferred by the public key (defaults to aes25
```js
const openpgp = require('openpgp') // use as CommonJS, AMD, ES6 module or via window.openpgp
openpgp.initWorker({ path:'openpgp.worker.js' }) // set the relative web worker path
await openpgp.initWorker({ path:'openpgp.worker.js' }) // set the relative web worker path
// put keys in backtick (``) to avoid errors caused by spaces or tabs
const pubkey = `-----BEGIN PGP PUBLIC KEY BLOCK-----

View File

@ -63,12 +63,19 @@ let asyncProxy; // instance of the asyncproxy
* @param {String} path relative path to the worker scripts, default: 'openpgp.worker.js'
* @param {Number} n number of workers to initialize
* @param {Array<Object>} workers alternative to path parameter: web workers initialized with 'openpgp.worker.js'
* @returns {Promise<Boolean>} returns a promise that resolves to true if all workers have succesfully finished loading
* @async
*/
export function initWorker({ path='openpgp.worker.js', n = 1, workers = [] } = {}) {
export async function initWorker({ path='openpgp.worker.js', n = 1, workers = [] } = {}) {
if (workers.length || (typeof window !== 'undefined' && window.Worker && window.MessageChannel)) {
asyncProxy = new AsyncProxy({ path, n, workers, config });
return true;
const proxy = new AsyncProxy({ path, n, workers, config });
const loaded = await proxy.loaded();
if (loaded) {
asyncProxy = proxy;
return true;
}
}
return false;
}
/**

View File

@ -49,6 +49,9 @@ function AsyncProxy({ path='openpgp.worker.js', n = 1, workers = [], config } =
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
@ -83,10 +86,15 @@ 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 => {
throw new Error('Unhandled error in openpgp worker: ' + e.message + ' (' + e.filename + ':' + e.lineno + ')');
worker.loadedResolve(false);
console.error('Unhandled error in openpgp worker: ' + e.message + ' (' + e.filename + ':' + e.lineno + ')');
return false;
};
if (config) {
@ -99,6 +107,15 @@ function AsyncProxy({ path='openpgp.worker.js', n = 1, workers = [], config } =
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);
};
/**
* Get new request ID
* @returns {integer} New unique request ID

View File

@ -135,3 +135,8 @@ function delegate(id, method, options) {
function response(event) {
self.postMessage(event, openpgp.util.getTransferables(event.data, true));
}
/**
* Let the main window know the worker has loaded.
*/
postMessage({ event: 'loaded' });

View File

@ -273,8 +273,8 @@ describe('Brainpool Cryptography', function () {
tryTests('Brainpool Worker Tests', omnibus, {
if: typeof window !== 'undefined' && window.Worker,
before: function() {
openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
before: async function() {
await openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
},
beforeEach: function() {
openpgp.config.use_native = true;

View File

@ -297,8 +297,8 @@ describe('Elliptic Curve Cryptography', function () {
tryTests('ECC Worker Tests', omnibus, {
if: typeof window !== 'undefined' && window.Worker,
before: function() {
openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
before: async function() {
await openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
},
beforeEach: function() {
openpgp.config.use_native = true;

View File

@ -444,13 +444,16 @@ describe('[Sauce Labs Group 2] OpenPGP.js public api tests', function() {
openpgp.destroyWorker(); // cleanup worker in case of failure
});
it('should work', function() {
it('should work', async function() {
const workerStub = {
postMessage: function() {}
};
openpgp.initWorker({
workers: [workerStub]
});
await Promise.all([
openpgp.initWorker({
workers: [workerStub]
}),
workerStub.onmessage({ data: { event: 'loaded' } })
]);
expect(openpgp.getWorker()).to.exist;
openpgp.destroyWorker();
expect(openpgp.getWorker()).to.not.exist;
@ -595,13 +598,16 @@ describe('[Sauce Labs Group 2] OpenPGP.js public api tests', function() {
});
});
it('should delegate to async proxy', function() {
it('should delegate to async proxy', async function() {
const workerStub = {
postMessage: function() {}
};
openpgp.initWorker({
workers: [workerStub]
});
await Promise.all([
openpgp.initWorker({
workers: [workerStub]
}),
workerStub.onmessage({ data: { event: 'loaded' } })
]);
const proxyGenStub = stub(openpgp.getWorker(), 'delegate');
getWebCryptoAllStub.returns();
@ -643,9 +649,9 @@ describe('[Sauce Labs Group 2] OpenPGP.js public api tests', function() {
});
});
it('should work in JS (with worker)', function() {
it('should work in JS (with worker)', async function() {
openpgp.config.use_native = false;
openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
await openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
const opt = {
userIds: [{ name: 'Test User', email: 'text@example.com' }],
numBits: 512
@ -727,11 +733,11 @@ describe('[Sauce Labs Group 2] OpenPGP.js public api tests', function() {
openpgp.config.aead_chunk_size_byte = aead_chunk_size_byteVal;
});
it('Configuration', function() {
it('Configuration', async function() {
openpgp.config.show_version = false;
openpgp.config.commentstring = 'different';
if (openpgp.getWorker()) { // init again to trigger config event
openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
await openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
}
return openpgp.encrypt({ publicKeys:publicKey.keys, message:openpgp.message.fromText(plaintext) }).then(function(encrypted) {
expect(encrypted.data).to.exist;
@ -749,7 +755,7 @@ describe('[Sauce Labs Group 2] OpenPGP.js public api tests', function() {
const { workers } = openpgp.getWorker();
try {
await privateKey.keys[0].decrypt(passphrase)
openpgp.initWorker({path: '../dist/openpgp.worker.js', workers, n: 2});
await openpgp.initWorker({path: '../dist/openpgp.worker.js', workers, n: 2});
const workerTest = (_, index) => {
const plaintext = input.createSomeMessage() + index;
@ -770,7 +776,7 @@ describe('[Sauce Labs Group 2] OpenPGP.js public api tests', function() {
};
await Promise.all(Array(10).fill(null).map(workerTest));
} finally {
openpgp.initWorker({path: '../dist/openpgp.worker.js', workers, n: 1 });
await openpgp.initWorker({path: '../dist/openpgp.worker.js', workers, n: 1 });
}
});
@ -828,8 +834,8 @@ describe('[Sauce Labs Group 2] OpenPGP.js public api tests', function() {
tryTests('CFB mode (asm.js, worker)', tests, {
if: typeof window !== 'undefined' && window.Worker,
before: function() {
openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
before: async function() {
await openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
},
beforeEach: function() {
openpgp.config.aead_protect = false;

View File

@ -319,8 +319,8 @@ describe('X25519 Cryptography', function () {
tryTests('X25519 Worker Tests', omnibus, {
if: typeof window !== 'undefined' && window.Worker,
before: function() {
openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
before: async function() {
await openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
},
beforeEach: function() {
openpgp.config.use_native = true;

View File

@ -37,7 +37,7 @@ let pubKey;
tryTests('Async Proxy', tests, {
if: typeof window !== 'undefined' && window.Worker && window.MessageChannel,
before: async function() {
openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
await openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
pubKey = (await openpgp.key.readArmored(pub_key)).keys[0];
},
after: function() {
@ -48,11 +48,13 @@ tryTests('Async Proxy', tests, {
function tests() {
describe('Random number pipeline', function() {
it('Random number buffer automatically reseeded', 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] });
return wProxy.delegate('encrypt', { publicKeys:[pubKey], message:openpgp.message.fromText(plaintext) });
const loaded = await wProxy.loaded();
if (loaded) {
return wProxy.delegate('encrypt', { publicKeys:[pubKey], message:openpgp.message.fromText(plaintext) });
}
});
});