🐛 Fix race condition in file download

This commit is contained in:
C-3PO 2018-07-08 19:09:31 +02:00
parent 662c2d5f43
commit 8b1ad6f7e2
Signed by: c3po
GPG key ID: 62993C4BB4D86F24
3 changed files with 11 additions and 13 deletions

View file

@ -15,9 +15,11 @@ export default function saveResponse(
//Remember file size
const headerLength = Number(response.headers['content-length']);
const writeStream = fs.createWriteStream(filePath);
//If we receive a part of the response, write it to disk
let previousChunk: Promise<void> = new Promise((innerResolve) => { innerResolve(); });
let totalLength = 0;
const chunkPromises: Array<Promise<void>> = [];
response.on('data', (chunk: Buffer) => {
totalLength += chunk.length;
@ -26,18 +28,12 @@ export default function saveResponse(
return reject(`Expected length ${headerLength} but received at least ${totalLength}.`);
}
//If previous chunk was not yet written to disk, wait until it finished to avoid a race condition
previousChunk.then(() => {
previousChunk = new Promise((innerResolve, innerReject) => {
//Write chunk to disk
fs.appendFile(filePath, chunk, { encoding: 'binary' }, (error) => {
if (error) {
return reject(`Could not write to disk: [${error.code}] ${error.name}: ${error.message}.`);
}
innerResolve();
});
});
chunkPromises.push(new Promise((writeResolve) => {
writeStream.write(chunk, () => {
writeResolve();
});
}));
});
//If we finished reading response, check for correctness, then return it
@ -50,7 +46,7 @@ export default function saveResponse(
//wait until everything is written to disk, then return file name
//TODO: need to automatically delete file once it is no longer used
//TODO: need to provide methods to seek through file
previousChunk.then(() => {
Promise.all(chunkPromises).then(() => {
resolve(filePath);
});
});

View file

@ -24,6 +24,7 @@ export default function extractFileStream(file: ISsnFileEntry, inputStream: stre
//pipe into decompression
const decompressTransform = zlib.createInflateRaw();
decompressTransform.on('error', (error) => {
//TODO: need to throw error sync, not async
throw new Error(`Error during decompression: ${error.message}`);
});
curStream = curStream.pipe(decompressTransform);

View file

@ -32,6 +32,7 @@ export default async function getPatch(product: Product, from: number, to: numbe
//Then we need to wait for disks to finish download before we can extract individual files
//TODO: we can optimize this to already extract some files as soon as their relevant parts are downloaded
const diskFilenames = await Promise.all(diskFiles);
console.debug(diskFilenames);
//const dvArray = bufferArray.map((buffer) => new DataView(buffer));
//TODO: Verify that downloaded files match the hash in `solidpkg.pieces`