|
| 1 | +'use strict'; |
| 2 | +// Regression test for https://github.com/nodejs/node/issues/63701 |
| 3 | +// BrotliCompress/BrotliDecompress.flush(kind) used to spin at 100% CPU when |
| 4 | +// kind was outside the brotli operation range (0..3) — e.g. Z_FINISH (4) or |
| 5 | +// Z_BLOCK (5). It must now throw ERR_OUT_OF_RANGE before reaching the native |
| 6 | +// layer. |
| 7 | + |
| 8 | +require('../common'); |
| 9 | +const assert = require('assert'); |
| 10 | +const zlib = require('zlib'); |
| 11 | + |
| 12 | +const { |
| 13 | + BROTLI_OPERATION_PROCESS, |
| 14 | + BROTLI_OPERATION_FLUSH, |
| 15 | + BROTLI_OPERATION_FINISH, |
| 16 | + BROTLI_OPERATION_EMIT_METADATA, |
| 17 | + Z_BLOCK, |
| 18 | + Z_FINISH, |
| 19 | +} = zlib.constants; |
| 20 | + |
| 21 | +// Brotli operations 0..3 are valid and must not throw synchronously from |
| 22 | +// flush(). Some operations (e.g. FINISH on a decoder with no input) emit a |
| 23 | +// Z_BUF_ERROR asynchronously — that's expected and unrelated to the input |
| 24 | +// validation under test, so we attach a noop error handler. |
| 25 | +for (const validKind of [ |
| 26 | + BROTLI_OPERATION_PROCESS, |
| 27 | + BROTLI_OPERATION_FLUSH, |
| 28 | + BROTLI_OPERATION_FINISH, |
| 29 | + BROTLI_OPERATION_EMIT_METADATA, |
| 30 | +]) { |
| 31 | + const c = zlib.createBrotliCompress(); |
| 32 | + c.on('error', () => {}); |
| 33 | + c.flush(validKind); |
| 34 | + |
| 35 | + const d = zlib.createBrotliDecompress(); |
| 36 | + d.on('error', () => {}); |
| 37 | + d.flush(validKind); |
| 38 | +} |
| 39 | + |
| 40 | +// Values outside [0, 3] must throw ERR_OUT_OF_RANGE for both compress and |
| 41 | +// decompress streams. Z_FINISH (4) and Z_BLOCK (5) previously hung at 100% CPU. |
| 42 | +const outOfRange = [-1, Z_FINISH, Z_BLOCK, 6, 7, 100]; |
| 43 | + |
| 44 | +for (const factory of [zlib.createBrotliCompress, zlib.createBrotliDecompress]) { |
| 45 | + for (const kind of outOfRange) { |
| 46 | + assert.throws( |
| 47 | + () => factory().flush(kind), |
| 48 | + { code: 'ERR_OUT_OF_RANGE', name: 'RangeError' }, |
| 49 | + ); |
| 50 | + } |
| 51 | +} |
| 52 | + |
| 53 | +// Non-number kinds must also throw, instead of silently triggering the |
| 54 | +// fake-chunk path with an undefined buffer. |
| 55 | +for (const factory of [zlib.createBrotliCompress, zlib.createBrotliDecompress]) { |
| 56 | + for (const kind of ['foobar', null, {}, NaN]) { |
| 57 | + assert.throws( |
| 58 | + () => factory().flush(kind), |
| 59 | + { code: 'ERR_OUT_OF_RANGE' }, |
| 60 | + ); |
| 61 | + } |
| 62 | +} |
| 63 | + |
| 64 | +// flush() with no arguments (or with only a callback) must still work — |
| 65 | +// it uses the stream's _defaultFullFlushFlag, which is a valid brotli op. |
| 66 | +zlib.createBrotliCompress().flush(); |
| 67 | +zlib.createBrotliCompress().flush(() => {}); |
| 68 | +zlib.createBrotliDecompress().flush(); |
| 69 | +zlib.createBrotliDecompress().flush(() => {}); |
0 commit comments