11'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.
75
86require ( '../common' ) ;
97const assert = require ( 'assert' ) ;
@@ -14,56 +12,81 @@ const {
1412 BROTLI_OPERATION_FLUSH ,
1513 BROTLI_OPERATION_FINISH ,
1614 BROTLI_OPERATION_EMIT_METADATA ,
15+ Z_NO_FLUSH ,
1716 Z_BLOCK ,
1817 Z_FINISH ,
18+ ZSTD_e_continue,
19+ ZSTD_e_flush,
20+ ZSTD_e_end,
1921} = zlib . constants ;
2022
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 = ( ) => { } ;
3424
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+ ] ;
3947
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+ }
4357
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+ }
5066 }
5167}
5268
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+ }
6177 }
6278}
6379
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+ }
0 commit comments