diff --git a/app/blobSlicer.js b/app/blobSlicer.js new file mode 100644 index 00000000..a13d97a3 --- /dev/null +++ b/app/blobSlicer.js @@ -0,0 +1,27 @@ +export default class BlobSlicer { + constructor(blob, size) { + this.blob = blob; + this.index = 0; + this.chunkSize = size; + } + + pull(controller) { + return new Promise((resolve, reject) => { + const bytesLeft = this.blob.size - this.index; + if (bytesLeft <= 0) { + controller.close(); + return resolve(); + } + const size = Math.min(this.chunkSize, bytesLeft); + const blob = this.blob.slice(this.index, this.index + size); + const reader = new FileReader(); + reader.onload = () => { + controller.enqueue(new Uint8Array(reader.result)); + resolve(); + }; + reader.onerror = reject; + reader.readAsArrayBuffer(blob); + this.index += size; + }); + } +} diff --git a/app/ece.js b/app/ece.js index 86ed29cd..952e0271 100644 --- a/app/ece.js +++ b/app/ece.js @@ -1,4 +1,5 @@ import 'buffer'; +import BlobSlicer from './blobSlicer'; import { transformStream } from './streams'; const NONCE_LENGTH = 12; @@ -225,40 +226,6 @@ class ECETransformer { } } -export class BlobSlicer { - constructor(blob, rs, mode) { - this.blob = blob; - this.index = 0; - this.mode = mode; - this.chunkSize = mode === MODE_ENCRYPT ? rs - 17 : rs; - } - - pull(controller) { - return new Promise((resolve, reject) => { - const bytesLeft = this.blob.size - this.index; - if (bytesLeft <= 0) { - controller.close(); - return resolve(); - } - let size = 1; - if (this.mode === MODE_DECRYPT && this.index === 0) { - size = Math.min(21, bytesLeft); - } else { - size = Math.min(this.chunkSize, bytesLeft); - } - const blob = this.blob.slice(this.index, this.index + size); - const reader = new FileReader(); - reader.onload = () => { - controller.enqueue(new Uint8Array(reader.result)); - resolve(); - }; - reader.onerror = reject; - reader.readAsArrayBuffer(blob); - this.index += size; - }); - } -} - class StreamSlicer { constructor(rs, mode) { this.mode = mode; @@ -350,7 +317,7 @@ export default class ECE { if (this.input instanceof Blob) { inputStream = new ReadableStream( - new BlobSlicer(this.input, this.rs, this.mode) + new BlobSlicer(this.input, this.rs - 17) ); } else { inputStream = transformStream( diff --git a/app/fileReceiver.js b/app/fileReceiver.js index e694245b..d9ede78a 100644 --- a/app/fileReceiver.js +++ b/app/fileReceiver.js @@ -148,7 +148,7 @@ export default class FileReceiver extends Nanobus { this.state = 'complete'; } catch (e) { this.downloadRequest = null; - if (e === 'cancelled') { + if (e === 'cancelled' || e.message === '400') { throw new Error(0); } throw e; diff --git a/app/serviceWorker.js b/app/serviceWorker.js index e2c5f271..ffe6be4c 100644 --- a/app/serviceWorker.js +++ b/app/serviceWorker.js @@ -15,8 +15,11 @@ self.addEventListener('activate', event => { }); async function decryptStream(id) { + const file = map.get(id); + if (!file) { + return new Response(null, { status: 400 }); + } try { - const file = map.get(id); const keychain = new Keychain(file.key, file.nonce); if (file.requiresPassword) { keychain.setPassword(file.password, file.url); diff --git a/test/frontend/tests/streaming-tests.js b/test/frontend/tests/streaming-tests.js index 44f898d8..2735f149 100644 --- a/test/frontend/tests/streaming-tests.js +++ b/test/frontend/tests/streaming-tests.js @@ -3,6 +3,7 @@ require('buffer'); import assert from 'assert'; import { b64ToArray } from '../../../app/utils'; +import BlobSlicer from '../../../app/blobSlicer'; import ECE from '../../../app/ece.js'; const rs = 36; @@ -47,8 +48,10 @@ describe('Streaming', function() { }); it('can decrypt', async function() { - const encBlob = new Blob([encrypted]); - const ece = new ECE(encBlob, key, 'decrypt', rs); + const blobStream = new ReadableStream( + new BlobSlicer(new Blob([encrypted]), rs) + ); + const ece = new ECE(blobStream, key, 'decrypt', rs); const decStream = await ece.transform(); const reader = decStream.getReader(); diff --git a/test/frontend/tests/workflow-tests.js b/test/frontend/tests/workflow-tests.js index 0ed96108..70123820 100644 --- a/test/frontend/tests/workflow-tests.js +++ b/test/frontend/tests/workflow-tests.js @@ -153,26 +153,26 @@ describe('Upload / Download flow', function() { assert.equal(file.dtotal, 1); }); - // it('does not increase download count when download cancelled', async function() { - // const fs = new FileSender(blob); - // const file = await fs.upload(); - // const fr = new FileReceiver({ - // secretKey: file.toJSON().secretKey, - // id: file.id, - // nonce: file.keychain.nonce, - // requiresPassword: false - // }); - // await fr.getMetadata(); - // fr.once('progress', () => fr.cancel()); + it('does not increase download count when download cancelled', async function() { + const fs = new FileSender(blob); + const file = await fs.upload(); + const fr = new FileReceiver({ + secretKey: file.toJSON().secretKey, + id: file.id, + nonce: file.keychain.nonce, + requiresPassword: false + }); + await fr.getMetadata(); + fr.once('progress', () => fr.cancel()); - // try { - // await fr.download(noSave); - // assert.fail('not cancelled'); - // } catch (e) { - // await file.updateDownloadCount(); - // assert.equal(file.dtotal, 0); - // } - // }); + try { + await fr.download(noSave); + assert.fail('not cancelled'); + } catch (e) { + await file.updateDownloadCount(); + assert.equal(file.dtotal, 0); + } + }); it('can allow multiple downloads', async function() { const fs = new FileSender(blob);