Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions lib/internal/zip/entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -501,15 +501,14 @@ class ZipEntry {
// The entry's raw compressed bytes as a bounded-memory chunk stream, read
// straight from disk (file-backed entries only). Nothing is retained.
async *#rawChunks() {
// Count the whole stream as one in-flight read so a concurrent close()
// waits for it (the finally runs when the consumer stops iterating); a
// stream begun after close() was requested is rejected up front.
// Count active reads, but not time paused at a yield.
const handle = this.#fd;
if (handle.closing) {
throw new ERR_INVALID_STATE(
'cannot read a ZipEntry after its backing ZipFile has been closed');
}
handle.reads++;
let reading = true;
try {
this.#liveDescriptor();
let pos = await this.#resolveContentOffset();
Expand All @@ -521,10 +520,18 @@ class ZipEntry {
await readFdFully(this.#liveDescriptor(), chunk, pos);
pos += take;
remaining -= take;
reading = false;
endHandleRead(handle);
yield chunk;
if (handle.closing) {
throw new ERR_INVALID_STATE(
'cannot read a ZipEntry after its backing ZipFile has been closed');
}
handle.reads++;
reading = true;
}
} finally {
endHandleRead(handle);
if (reading) endHandleRead(handle);
}
}
// Sync counterpart of #rawChunks().
Expand Down
15 changes: 15 additions & 0 deletions test/parallel/test-zlib-zip-file-lifecycle.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,21 @@ test('close() waits for an in-flight read instead of closing under it', async ()
assert.ok(data.equals(payload));
});

test('close() resolves with a paused contentIterator()', async () => {
tmpdir.refresh();
const file = tmpdir.resolve('paused-iterator.zip');
const payload = Buffer.alloc(8 * 1024 * 1024, 0x61);
writeArchive(file, [zlib.ZipEntry.createSync('big', payload, { method: 'store' })]);

const zf = zlib.ZipFile.openSync(file);
const iterator = zf.getSync('big').contentIterator();
const first = await iterator.next();
assert.strictEqual(first.done, false);

await zf.close();
await assert.rejects(iterator.next(), { code: 'ERR_INVALID_STATE' });
});

// 2. A failed central-directory rewrite during add() is rolled back.
test('a failed directory rewrite during addEntrySync is rolled back', () => {
tmpdir.refresh();
Expand Down
Loading