chore: small re-factor, and update changeset

Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
blam
2022-07-14 13:02:49 +02:00
parent 773fc2c4a5
commit e27ada575f
3 changed files with 50 additions and 60 deletions
+1 -1
View File
@@ -6,4 +6,4 @@ The `ZipArchiveResponse` now correctly handles corrupt ZIP archives.
Before this change, certain corrupt ZIP archives either cause the inflater to throw (as expected), or will hang the parser indefinitely.
This change introduces a default timeout of 3000ms before throwing an error message when trying to unzip an archive.
By switching out the `zip` parsing library, we now write to a temporary directory, and load from disk which should ensure that the parsing of the `.zip` files are done correctly because `streaming` of `zip` paths is technically impossible without being able to parse the headers at the end of he file.
+1 -3
View File
@@ -49,7 +49,6 @@
"@types/express": "^4.17.6",
"@types/luxon": "^2.0.4",
"@types/webpack-env": "^1.15.2",
"@types/yauzl": "^2.10.0",
"archiver": "^5.0.2",
"aws-sdk": "^2.840.0",
"base64-stream": "^1.0.0",
@@ -79,7 +78,6 @@
"selfsigned": "^2.0.0",
"stoppable": "^1.1.0",
"tar": "^6.1.2",
"unzipper": "^0.10.11",
"winston": "^3.2.1",
"yauzl": "^2.10.0",
"yn": "^4.0.0"
@@ -108,7 +106,7 @@
"@types/stoppable": "^1.1.0",
"@types/supertest": "^2.0.8",
"@types/tar": "^6.1.1",
"@types/unzipper": "^0.10.3",
"@types/yauzl": "^2.10.0",
"@types/webpack-env": "^1.15.2",
"aws-sdk-mock": "^5.2.1",
"better-sqlite3": "^7.5.0",
@@ -80,6 +80,54 @@ export class ZipArchiveResponse implements ReadTreeResponse {
return true;
}
private async streamToTemporaryFile(stream: Readable): Promise<string> {
const tmpDir = await fs.mkdtemp(
platformPath.join(this.workDir, 'backstage-tmp'),
);
const tmpFile = platformPath.join(tmpDir, 'tmp.zip');
const writeStream = fs.createWriteStream(tmpFile);
return new Promise((resolve, reject) => {
writeStream.on('error', reject);
writeStream.on('finish', () => resolve(tmpFile));
stream.pipe(writeStream);
});
}
private forEveryZipEntry(
zip: string,
callback: (entry: Entry, content: Readable) => Promise<void>,
): Promise<void> {
return new Promise((resolve, reject) => {
yauzl.open(zip, { lazyEntries: true }, (err, zipfile) => {
if (err || !zipfile) {
reject(err || new Error(`Failed to open zip file ${zip}`));
return;
}
zipfile.on('entry', async (entry: Entry) => {
if (!/\/$/.test(entry.fileName) && this.shouldBeIncluded(entry)) {
zipfile.openReadStream(entry, async (openErr, readStream) => {
if (openErr || !readStream) {
reject(
openErr ||
new Error(`Failed to open zip entry ${entry.fileName}`),
);
return;
}
await callback(entry, readStream);
});
}
zipfile.readEntry();
});
zipfile.once('end', () => resolve());
zipfile.readEntry();
});
});
}
async files(): Promise<ReadTreeResponseFile[]> {
this.onlyOnce();
const files = Array<ReadTreeResponseFile>();
@@ -116,62 +164,6 @@ export class ZipArchiveResponse implements ReadTreeResponse {
return archive;
}
private async streamToTemporaryFile(stream: Readable): Promise<string> {
const tmpDir = await fs.mkdtemp(
platformPath.join(this.workDir, 'backstage-tmp'),
);
const tmpFile = platformPath.join(tmpDir, 'tmp.zip');
const writeStream = fs.createWriteStream(tmpFile);
return new Promise((resolve, reject) => {
writeStream.on('error', reject);
writeStream.on('finish', () => resolve(tmpFile));
stream.pipe(writeStream);
});
}
private forEveryZipEntry(
zip: string,
callback: (entry: Entry, content: Readable) => Promise<void>,
): Promise<void> {
return new Promise((resolve, reject) => {
yauzl.open(zip, { lazyEntries: true }, (err, zipfile) => {
if (err) {
reject(err);
return;
}
if (!zipfile) {
reject(new Error('Zip file is empty'));
return;
}
zipfile.on('entry', async (entry: Entry) => {
// If it's not a directory, and it's included, then grab the contents of the file from the buffer
if (!/\/$/.test(entry.fileName) && this.shouldBeIncluded(entry)) {
zipfile.openReadStream(entry, async (openErr, readStream) => {
if (openErr) {
reject(openErr);
return;
}
if (!readStream) {
reject(new Error('Zip file is empty'));
return;
}
await callback(entry, readStream);
});
}
zipfile.readEntry();
});
zipfile.once('end', () => resolve());
zipfile.readEntry();
});
});
}
async dir(options?: ReadTreeResponseDirOptions): Promise<string> {
this.onlyOnce();
const dir =