| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 928981d commit 9a20866
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -358,17 +358,10 @@ ZlibBase.prototype.flush = function(kind, callback) { | |||
| 358 | 358 | kind = this._defaultFullFlushFlag; | |
| 359 | 359 | } | |
| 360 | 360 | ||
| 361 | - // Reject kinds outside the brotli operation range. Otherwise the fake-chunk | ||
| 362 | - // path forwards an unsupported flush flag to the native encoder/decoder, | ||
| 363 | - // which spins at 100% CPU without making progress (see #63701). | ||
| 364 | - if (this._flushBoundIdx === FLUSH_BOUND_IDX_BROTLI) { | ||
| 365 | - const min = FLUSH_BOUND[FLUSH_BOUND_IDX_BROTLI][0]; | ||
| 366 | - const max = FLUSH_BOUND[FLUSH_BOUND_IDX_BROTLI][1]; | ||
| 367 | - if (typeof kind !== 'number' || NumberIsNaN(kind) || | ||
| 368 | - kind < min || kind > max) { | ||
| 369 | - throw new ERR_OUT_OF_RANGE('kind', `>= ${min} and <= ${max}`, kind); | ||
| 370 | - } | ||
| 371 | - } | ||
| 361 | + kind = checkRangesOrGetDefault( | ||
| 362 | + kind, 'kind', | ||
| 363 | + FLUSH_BOUND[this._flushBoundIdx][0], FLUSH_BOUND[this._flushBoundIdx][1], | ||
| 364 | + this._defaultFullFlushFlag); | ||
| 372 | 365 | ||
| 373 | 366 | if (this.writableFinished) { | |
| 374 | 367 | if (callback) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,9 +1,7 @@ | |||
| 1 | 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. | ||
| 2 | + // Regression test for https://github.com/nodejs/node/issues/63701. | ||
| 3 | + // Invalid Brotli flush kinds used to spin in native code. flush(kind) should | ||
| 4 | + // reject invalid kinds before writing the fake flush chunk. | ||
| 7 | 5 | ||
| 8 | 6 | require('../common'); | |
| 9 | 7 | const assert = require('assert'); | |
@@ -14,56 +12,81 @@ const { | |||
| 14 | 12 | BROTLI_OPERATION_FLUSH, | |
| 15 | 13 | BROTLI_OPERATION_FINISH, | |
| 16 | 14 | BROTLI_OPERATION_EMIT_METADATA, | |
| 15 | + Z_NO_FLUSH, | ||
| 17 | 16 | Z_BLOCK, | |
| 18 | 17 | Z_FINISH, | |
| 18 | + ZSTD_e_continue, | ||
| 19 | + ZSTD_e_flush, | ||
| 20 | + ZSTD_e_end, | ||
| 19 | 21 | } = zlib.constants; | |
| 20 | 22 | ||
| 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); | ||
| 23 | + const noop = () => {}; | ||
| 34 | 24 | ||
| 35 | - const d = zlib.createBrotliDecompress(); | ||
| 36 | - d.on('error', () => {}); | ||
| 37 | - d.flush(validKind); | ||
| 38 | - } | ||
| 25 | + const flushKindTestCases = [ | ||
| 26 | + { | ||
| 27 | + factories: [zlib.createGzip], | ||
| 28 | + validKinds: [Z_NO_FLUSH, Z_FINISH, Z_BLOCK], | ||
| 29 | + invalidKinds: [-1, 6, 100], | ||
| 30 | + }, | ||
| 31 | + { | ||
| 32 | + factories: [zlib.createBrotliCompress, zlib.createBrotliDecompress], | ||
| 33 | + validKinds: [ | ||
| 34 | + BROTLI_OPERATION_PROCESS, | ||
| 35 | + BROTLI_OPERATION_FLUSH, | ||
| 36 | + BROTLI_OPERATION_FINISH, | ||
| 37 | + BROTLI_OPERATION_EMIT_METADATA, | ||
| 38 | + ], | ||
| 39 | + invalidKinds: [-1, Z_FINISH, Z_BLOCK, 6, 100], | ||
| 40 | + }, | ||
| 41 | + { | ||
| 42 | + factories: [zlib.createZstdCompress, zlib.createZstdDecompress], | ||
| 43 | + validKinds: [ZSTD_e_continue, ZSTD_e_flush, ZSTD_e_end], | ||
| 44 | + invalidKinds: [-1, 3, Z_FINISH, Z_BLOCK, 100], | ||
| 45 | + }, | ||
| 46 | + ]; | ||
| 39 | 47 | ||
| 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]; | ||
| 48 | + for (const { factories, validKinds } of flushKindTestCases) { | ||
| 49 | + for (const factory of factories) { | ||
| 50 | + for (const kind of validKinds) { | ||
| 51 | + const stream = factory(); | ||
| 52 | + stream.on('error', noop); | ||
| 53 | + stream.flush(kind); | ||
| 54 | + } | ||
| 55 | + } | ||
| 56 | + } | ||
| 43 | 57 | ||
| 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 | - ); | ||
| 58 | + for (const { factories, invalidKinds } of flushKindTestCases) { | ||
| 59 | + for (const factory of factories) { | ||
| 60 | + for (const kind of invalidKinds) { | ||
| 61 | + assert.throws( | ||
| 62 | + () => factory().flush(kind), | ||
| 63 | + { code: 'ERR_OUT_OF_RANGE', name: 'RangeError' }, | ||
| 64 | + ); | ||
| 65 | + } | ||
| 50 | 66 | } | |
| 51 | 67 | } | |
| 52 | 68 | ||
| 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 | - ); | ||
| 69 | + for (const { factories } of flushKindTestCases) { | ||
| 70 | + for (const factory of factories) { | ||
| 71 | + for (const kind of ['foobar', null, {}]) { | ||
| 72 | + assert.throws( | ||
| 73 | + () => factory().flush(kind), | ||
| 74 | + { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }, | ||
| 75 | + ); | ||
| 76 | + } | ||
| 61 | 77 | } | |
| 62 | 78 | } | |
| 63 | 79 | ||
| 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(() => {}); | ||
| 80 | + for (const { factories } of flushKindTestCases) { | ||
| 81 | + for (const factory of factories) { | ||
| 82 | + for (const kind of [undefined, NaN]) { | ||
| 83 | + const stream = factory(); | ||
| 84 | + stream.on('error', noop); | ||
| 85 | + stream.flush(kind); | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + const stream = factory(); | ||
| 89 | + stream.on('error', noop); | ||
| 90 | + stream.flush(noop); | ||
| 91 | + } | ||
| 92 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments