Improve stream error safety

GitOrigin-RevId: de4c512a62d304b3eeb2a1521aac173fa93d8411
This commit is contained in:
andrew rumble 2024-06-18 09:19:08 +01:00 committed by Copybot
parent 1409e32010
commit 487d9125a2
3 changed files with 26 additions and 11 deletions

View file

@ -1,5 +1,6 @@
const OutputFileArchiveManager = require('./OutputFileArchiveManager')
const { expressify } = require('@overleaf/promise-utils')
const { pipeline } = require('node:stream/promises')
async function createOutputZip(req, res) {
const {
@ -16,7 +17,7 @@ async function createOutputZip(req, res) {
res.attachment('output.zip')
res.setHeader('X-Content-Type-Options', 'nosniff')
archive.pipe(res)
await pipeline(archive, res)
}
module.exports = { createOutputZip: expressify(createOutputZip) }

View file

@ -34,13 +34,22 @@ module.exports = {
const missingFiles = []
for (const file of validFiles) {
let fileHandle
try {
const fileHandle = await open(file, 'r')
const fileStream = fileHandle.createReadStream()
archive.append(fileStream, { name: path.basename(file) })
fileHandle = await open(file, 'r')
} catch (error) {
logger.warn(
{ file, error },
'error opening file to add to output files archive'
)
missingFiles.push(file)
continue
}
const fileStream = fileHandle.createReadStream()
archive.append(fileStream, {
name: path.basename(file),
})
}
if (missingFiles.length > 0) {

View file

@ -8,10 +8,9 @@ const MODULE_PATH = require('path').join(
describe('OutputController', function () {
describe('createOutputZip', function () {
beforeEach(function () {
this.archive = {
on: sinon.stub(),
pipe: sinon.stub(),
}
this.archive = {}
this.pipeline = sinon.stub().resolves()
this.archiveFilesForBuild = sinon.stub().resolves(this.archive)
@ -20,6 +19,9 @@ describe('OutputController', function () {
'./OutputFileArchiveManager': {
archiveFilesForBuild: this.archiveFilesForBuild,
},
'node:stream/promises': {
pipeline: this.pipeline,
},
},
})
})
@ -40,12 +42,15 @@ describe('OutputController', function () {
files: ['output.tex'],
},
}
this.archive.pipe.callsFake(() => done())
this.pipeline.callsFake(() => {
done()
return Promise.resolve()
})
this.OutputController.createOutputZip(this.req, this.res)
})
it('pipes the archive to the response', function () {
sinon.assert.calledWith(this.archive.pipe, this.res)
it('creates a pipeline from the archive to the response', function () {
sinon.assert.calledWith(this.pipeline, this.archive, this.res)
})
it('calls the express convenience method to set attachment headers', function () {