diff --git a/README.md b/README.md index c90e4019..bc320419 100644 --- a/README.md +++ b/README.md @@ -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----- diff --git a/src/openpgp.js b/src/openpgp.js index 436d7a54..435b0ad0 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -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} workers alternative to path parameter: web workers initialized with 'openpgp.worker.js' + * @returns {Promise} 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; } /** diff --git a/src/worker/async_proxy.js b/src/worker/async_proxy.js index 35ed6990..ce809206 100644 --- a/src/worker/async_proxy.js +++ b/src/worker/async_proxy.js @@ -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} 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 diff --git a/src/worker/worker.js b/src/worker/worker.js index 845b1a6b..a9281372 100644 --- a/src/worker/worker.js +++ b/src/worker/worker.js @@ -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' }); diff --git a/test/general/brainpool.js b/test/general/brainpool.js index 1a50ce3c..a8a2a9c6 100644 --- a/test/general/brainpool.js +++ b/test/general/brainpool.js @@ -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; diff --git a/test/general/ecc_nist.js b/test/general/ecc_nist.js index e6faff65..f079dd16 100644 --- a/test/general/ecc_nist.js +++ b/test/general/ecc_nist.js @@ -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; diff --git a/test/general/openpgp.js b/test/general/openpgp.js index fa2f04f5..24cf3741 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -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; diff --git a/test/general/x25519.js b/test/general/x25519.js index 660b7054..ab78dfec 100644 --- a/test/general/x25519.js +++ b/test/general/x25519.js @@ -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; diff --git a/test/worker/async_proxy.js b/test/worker/async_proxy.js index ed8d30a4..0fe02bb0 100644 --- a/test/worker/async_proxy.js +++ b/test/worker/async_proxy.js @@ -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) }); + } }); });