stream: yield expected Error class on zlib errors

PR-URL: https://github.com/nodejs/node/pull/50712
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
Filip Skokan 2023-11-13 14:30:50 +01:00 committed by Node.js GitHub Bot
parent 61b7846431
commit 2aff5cf638
2 changed files with 42 additions and 23 deletions

View File

@ -1,13 +1,19 @@
'use strict'; 'use strict';
const { const {
ArrayPrototypeFilter,
ArrayPrototypeMap,
Boolean,
ObjectEntries,
PromisePrototypeThen, PromisePrototypeThen,
PromiseResolve, PromiseResolve,
SafePromiseAll, SafePromiseAll,
SafePromisePrototypeFinally, SafePromisePrototypeFinally,
SafeSet,
TypedArrayPrototypeGetBuffer, TypedArrayPrototypeGetBuffer,
TypedArrayPrototypeGetByteOffset,
TypedArrayPrototypeGetByteLength, TypedArrayPrototypeGetByteLength,
TypedArrayPrototypeGetByteOffset,
TypeError,
Uint8Array, Uint8Array,
} = primordials; } = primordials;
@ -82,6 +88,38 @@ const { UV_EOF } = internalBinding('uv');
const encoder = new TextEncoder(); const encoder = new TextEncoder();
// Collect all negative (error) ZLIB codes and Z_NEED_DICT
const ZLIB_FAILURES = new SafeSet([
...ArrayPrototypeFilter(
ArrayPrototypeMap(
ObjectEntries(internalBinding('constants').zlib),
({ 0: code, 1: value }) => (value < 0 ? code : null),
),
Boolean,
),
'Z_NEED_DICT',
]);
/**
* @param {Error|null} cause
* @returns {Error|null}
*/
function handleKnownInternalErrors(cause) {
switch (true) {
case cause?.code === 'ERR_STREAM_PREMATURE_CLOSE': {
return new AbortError(undefined, { cause });
}
case ZLIB_FAILURES.has(cause?.code): {
// eslint-disable-next-line no-restricted-syntax
const error = new TypeError(undefined, { cause });
error.code = cause.code;
return error;
}
default:
return cause;
}
}
/** /**
* @typedef {import('../../stream').Writable} Writable * @typedef {import('../../stream').Writable} Writable
* @typedef {import('../../stream').Readable} Readable * @typedef {import('../../stream').Readable} Readable
@ -137,10 +175,7 @@ function newWritableStreamFromStreamWritable(streamWritable) {
} }
const cleanup = finished(streamWritable, (error) => { const cleanup = finished(streamWritable, (error) => {
if (error?.code === 'ERR_STREAM_PREMATURE_CLOSE') { error = handleKnownInternalErrors(error);
const err = new AbortError(undefined, { cause: error });
error = err;
}
cleanup(); cleanup();
// This is a protection against non-standard, legacy streams // This is a protection against non-standard, legacy streams
@ -440,10 +475,7 @@ function newReadableStreamFromStreamReadable(streamReadable, options = kEmptyObj
streamReadable.pause(); streamReadable.pause();
const cleanup = finished(streamReadable, (error) => { const cleanup = finished(streamReadable, (error) => {
if (error?.code === 'ERR_STREAM_PREMATURE_CLOSE') { error = handleKnownInternalErrors(error);
const err = new AbortError(undefined, { cause: error });
error = err;
}
cleanup(); cleanup();
// This is a protection against non-standard, legacy streams // This is a protection against non-standard, legacy streams

View File

@ -14,21 +14,8 @@
"decompression-corrupt-input.tentative.any.js": { "decompression-corrupt-input.tentative.any.js": {
"fail": { "fail": {
"expected": [ "expected": [
"truncating the input for 'deflate' should give an error",
"trailing junk for 'deflate' should give an error", "trailing junk for 'deflate' should give an error",
"format 'deflate' field CMF should be error for 0", "trailing junk for 'gzip' should give an error"
"format 'deflate' field FLG should be error for 157",
"format 'deflate' field DATA should be error for 5",
"format 'deflate' field ADLER should be error for 255",
"truncating the input for 'gzip' should give an error",
"trailing junk for 'gzip' should give an error",
"format 'gzip' field ID should be error for 255",
"format 'gzip' field CM should be error for 0",
"format 'gzip' field FLG should be error for 2",
"format 'gzip' field DATA should be error for 3",
"format 'gzip' field CRC should be error for 0",
"format 'gzip' field ISIZE should be error for 1",
"the deflate input compressed with dictionary should give an error"
] ]
} }
}, },