File
Blob: src/workerd/api/node/tests/zlib-nodejs-test.js
| 1 | // Copyright (c) 2024 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | import assert from 'node:assert'; |
| 5 | import { Buffer } from 'node:buffer'; |
| 6 | import { |
| 7 | Readable, |
| 8 | Writable, |
| 9 | Stream, |
| 10 | getDefaultHighWaterMark, |
| 11 | } from 'node:stream'; |
| 12 | import { inspect, promisify } from 'node:util'; |
| 13 | import { mock } from 'node:test'; |
| 14 | import zlib from 'node:zlib'; |
| 15 | import crypto from 'node:crypto'; |
| 16 | |
| 17 | // The following test data comes from |
| 18 | // https://github.com/zlib-ng/zlib-ng/blob/5401b24/test/test_crc32.cc |
| 19 | // test_crc32.cc -- crc32 unit test |
| 20 | // Copyright (C) 2019-2021 IBM Corporation |
| 21 | // Authors: Rogerio Alves <rogealve@br.ibm.com> |
| 22 | // Matheus Castanho <msc@linux.ibm.com> |
| 23 | // For conditions of distribution and use, see copyright notice in zlib.h |
| 24 | export const crc32Test = { |
| 25 | test() { |
| 26 | const tests = [ |
| 27 | [0x0, 0x0, 0, 0x0], |
| 28 | [0xffffffff, 0x0, 0, 0x0], |
| 29 | [0x0, 0x0, 255, 0x0] /* BZ 174799. */, |
| 30 | [0x0, 0x0, 256, 0x0], |
| 31 | [0x0, 0x0, 257, 0x0], |
| 32 | [0x0, 0x0, 32767, 0x0], |
| 33 | [0x0, 0x0, 32768, 0x0], |
| 34 | [0x0, 0x0, 32769, 0x0], |
| 35 | [0x0, '', 0, 0x0], |
| 36 | [0xffffffff, '', 0, 0xffffffff], |
| 37 | [0x0, 'abacus', 6, 0xc3d7115b], |
| 38 | [0x0, 'backlog', 7, 0x269205], |
| 39 | [0x0, 'campfire', 8, 0x22a515f8], |
| 40 | [0x0, 'delta', 5, 0x9643fed9], |
| 41 | [0x0, 'executable', 10, 0xd68eda01], |
| 42 | [0x0, 'file', 4, 0x8c9f3610], |
| 43 | [0x0, 'greatest', 8, 0xc1abd6cd], |
| 44 | [0x0, 'hello', 5, 0x3610a686], |
| 45 | [0x0, 'inverter', 8, 0xc9e962c9], |
| 46 | [0x0, 'jigsaw', 6, 0xce4e3f69], |
| 47 | [0x0, 'karate', 6, 0x890be0e2], |
| 48 | [0x0, 'landscape', 9, 0xc4e0330b], |
| 49 | [0x0, 'machine', 7, 0x1505df84], |
| 50 | [0x0, 'nanometer', 9, 0xd4e19f39], |
| 51 | [0x0, 'oblivion', 8, 0xdae9de77], |
| 52 | [0x0, 'panama', 6, 0x66b8979c], |
| 53 | [0x0, 'quest', 5, 0x4317f817], |
| 54 | [0x0, 'resource', 8, 0xbc91f416], |
| 55 | [0x0, 'secret', 6, 0x5ca2e8e5], |
| 56 | [0x0, 'test', 4, 0xd87f7e0c], |
| 57 | [0x0, 'ultimate', 8, 0x3fc79b0b], |
| 58 | [0x0, 'vector', 6, 0x1b6e485b], |
| 59 | [0x0, 'walrus', 6, 0xbe769b97], |
| 60 | [0x0, 'xeno', 4, 0xe7a06444], |
| 61 | [0x0, 'yelling', 7, 0xfe3944e5], |
| 62 | [0x0, 'zlib', 4, 0x73887d3a], |
| 63 | [0x0, '4BJD7PocN1VqX0jXVpWB', 20, 0xd487a5a1], |
| 64 | [0x0, 'F1rPWI7XvDs6nAIRx41l', 20, 0x61a0132e], |
| 65 | [0x0, 'ldhKlsVkPFOveXgkGtC2', 20, 0xdf02f76], |
| 66 | [0x0, '5KKnGOOrs8BvJ35iKTOS', 20, 0x579b2b0a], |
| 67 | [0x0, '0l1tw7GOcem06Ddu7yn4', 20, 0xf7d16e2d], |
| 68 | [0x0, 'MCr47CjPIn9R1IvE1Tm5', 20, 0x731788f5], |
| 69 | [0x0, 'UcixbzPKTIv0SvILHVdO', 20, 0x7112bb11], |
| 70 | [0x0, 'dGnAyAhRQDsWw0ESou24', 20, 0xf32a0dac], |
| 71 | [0x0, 'di0nvmY9UYMYDh0r45XT', 20, 0x625437bb], |
| 72 | [0x0, '2XKDwHfAhFsV0RhbqtvH', 20, 0x896930f9], |
| 73 | [0x0, 'ZhrANFIiIvRnqClIVyeD', 20, 0x8579a37], |
| 74 | [0x0, 'v7Q9ehzioTOVeDIZioT1', 20, 0x632aa8e0], |
| 75 | [0x0, 'Yod5hEeKcYqyhfXbhxj2', 20, 0xc829af29], |
| 76 | [0x0, 'GehSWY2ay4uUKhehXYb0', 20, 0x1b08b7e8], |
| 77 | [0x0, 'kwytJmq6UqpflV8Y8GoE', 20, 0x4e33b192], |
| 78 | [0x0, '70684206568419061514', 20, 0x59a179f0], |
| 79 | [0x0, '42015093765128581010', 20, 0xcd1013d7], |
| 80 | [0x0, '88214814356148806939', 20, 0xab927546], |
| 81 | [0x0, '43472694284527343838', 20, 0x11f3b20c], |
| 82 | [0x0, '49769333513942933689', 20, 0xd562d4ca], |
| 83 | [0x0, '54979784887993251199', 20, 0x233395f7], |
| 84 | [0x0, '58360544869206793220', 20, 0x2d167fd5], |
| 85 | [0x0, '27347953487840714234', 20, 0x8b5108ba], |
| 86 | [0x0, '07650690295365319082', 20, 0xc46b3cd8], |
| 87 | [0x0, '42655507906821911703', 20, 0xc10b2662], |
| 88 | [0x0, '29977409200786225655', 20, 0xc9a0f9d2], |
| 89 | [0x0, '85181542907229116674', 20, 0x9341357b], |
| 90 | [0x0, '87963594337989416799', 20, 0xf0424937], |
| 91 | [0x0, '21395988329504168551', 20, 0xd7c4c31f], |
| 92 | [0x0, '51991013580943379423', 20, 0xf11edcc4], |
| 93 | [0x0, '*]+@!);({_$;}[_},?{?;(_?,=-][@', 30, 0x40795df4], |
| 94 | [0x0, '_@:_).&(#.[:[{[:)$++-($_;@[)}+', 30, 0xdd61a631], |
| 95 | [0x0, '&[!,[$_==}+.]@!;*(+},[;:)$;)-@', 30, 0xca907a99], |
| 96 | [0x0, ']{.[.+?+[[=;[?}_#&;[=)__$$:+=_', 30, 0xf652deac], |
| 97 | [0x0, '-%.)=/[@].:.(:,()$;=%@-$?]{%+%', 30, 0xaf39a5a9], |
| 98 | [0x0, '+]#$(@&.=:,*];/.!]%/{:){:@(;)$', 30, 0x6bebb4cf], |
| 99 | [0x0, ')-._.:?[&:.=+}(*$/=!.${;(=$@!}', 30, 0x76430bac], |
| 100 | [0x0, ':(_*&%/[[}+,?#$&*+#[([*-/#;%(]', 30, 0x6c80c388], |
| 101 | [0x0, '{[#-;:$/{)(+[}#]/{&!%(@)%:@-$:', 30, 0xd54d977d], |
| 102 | [0x0, '_{$*,}(&,@.)):=!/%(&(,,-?$}}}!', 30, 0xe3966ad5], |
| 103 | [ |
| 104 | 0x0, |
| 105 | 'e$98KNzqaV)Y:2X?]77].{gKRD4G5{mHZk,Z)SpU%L3FSgv!Wb8MLAFdi{+fp)c,@8m6v)yXg@]HBDFk?.4&}g5_udE*JHCiH=aL', |
| 106 | 100, |
| 107 | 0xe7c71db9, |
| 108 | ], |
| 109 | [ |
| 110 | 0x0, |
| 111 | 'r*Fd}ef+5RJQ;+W=4jTR9)R*p!B;]Ed7tkrLi;88U7g@3v!5pk2X6D)vt,.@N8c]@yyEcKi[vwUu@.Ppm@C6%Mv*3Nw}Y,58_aH)', |
| 112 | 100, |
| 113 | 0xeaa52777, |
| 114 | ], |
| 115 | [ |
| 116 | 0x0, |
| 117 | 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&', |
| 118 | 100, |
| 119 | 0xcd472048, |
| 120 | ], |
| 121 | [0x7a30360d, 'abacus', 6, 0xf8655a84], |
| 122 | [0x6fd767ee, 'backlog', 7, 0x1ed834b1], |
| 123 | [0xefeb7589, 'campfire', 8, 0x686cfca], |
| 124 | [0x61cf7e6b, 'delta', 5, 0x1554e4b1], |
| 125 | [0xdc712e2, 'executable', 10, 0x761b4254], |
| 126 | [0xad23c7fd, 'file', 4, 0x7abdd09b], |
| 127 | [0x85cb2317, 'greatest', 8, 0x4ba91c6b], |
| 128 | [0x9eed31b0, 'inverter', 8, 0xd5e78ba5], |
| 129 | [0xb94f34ca, 'jigsaw', 6, 0x23649109], |
| 130 | [0xab058a2, 'karate', 6, 0xc5591f41], |
| 131 | [0x5bff2b7a, 'landscape', 9, 0xf10eb644], |
| 132 | [0x605c9a5f, 'machine', 7, 0xbaa0a636], |
| 133 | [0x51bdeea5, 'nanometer', 9, 0x6af89afb], |
| 134 | [0x85c21c79, 'oblivion', 8, 0xecae222b], |
| 135 | [0x97216f56, 'panama', 6, 0x47dffac4], |
| 136 | [0x18444af2, 'quest', 5, 0x70c2fe36], |
| 137 | [0xbe6ce359, 'resource', 8, 0x1471d925], |
| 138 | [0x843071f1, 'secret', 6, 0x50c9a0db], |
| 139 | [0xf2480c60, 'ultimate', 8, 0xf973daf8], |
| 140 | [0x2d2feb3d, 'vector', 6, 0x344ac03d], |
| 141 | [0x7490310a, 'walrus', 6, 0x6d1408ef], |
| 142 | [0x97d247d4, 'xeno', 4, 0xe62670b5], |
| 143 | [0x93cf7599, 'yelling', 7, 0x1b36da38], |
| 144 | [0x73c84278, 'zlib', 4, 0x6432d127], |
| 145 | [0x228a87d1, '4BJD7PocN1VqX0jXVpWB', 20, 0x997107d0], |
| 146 | [0xa7a048d0, 'F1rPWI7XvDs6nAIRx41l', 20, 0xdc567274], |
| 147 | [0x1f0ded40, 'ldhKlsVkPFOveXgkGtC2', 20, 0xdcc63870], |
| 148 | [0xa804a62f, '5KKnGOOrs8BvJ35iKTOS', 20, 0x6926cffd], |
| 149 | [0x508fae6a, '0l1tw7GOcem06Ddu7yn4', 20, 0xb52b38bc], |
| 150 | [0xe5adaf4f, 'MCr47CjPIn9R1IvE1Tm5', 20, 0xf83b8178], |
| 151 | [0x67136a40, 'UcixbzPKTIv0SvILHVdO', 20, 0xc5213070], |
| 152 | [0xb00c4a10, 'dGnAyAhRQDsWw0ESou24', 20, 0xbc7648b0], |
| 153 | [0x2e0c84b5, 'di0nvmY9UYMYDh0r45XT', 20, 0xd8123a72], |
| 154 | [0x81238d44, '2XKDwHfAhFsV0RhbqtvH', 20, 0xd5ac5620], |
| 155 | [0xf853aa92, 'ZhrANFIiIvRnqClIVyeD', 20, 0xceae099d], |
| 156 | [0x5a692325, 'v7Q9ehzioTOVeDIZioT1', 20, 0xb07d2b24], |
| 157 | [0x3275b9f, 'Yod5hEeKcYqyhfXbhxj2', 20, 0x24ce91df], |
| 158 | [0x38371feb, 'GehSWY2ay4uUKhehXYb0', 20, 0x707b3b30], |
| 159 | [0xafc8bf62, 'kwytJmq6UqpflV8Y8GoE', 20, 0x16abc6a9], |
| 160 | [0x9b07db73, '70684206568419061514', 20, 0xae1fb7b7], |
| 161 | [0xe75b214, '42015093765128581010', 20, 0xd4eecd2d], |
| 162 | [0x72d0fe6f, '88214814356148806939', 20, 0x4660ec7], |
| 163 | [0xf857a4b1, '43472694284527343838', 20, 0xfd8afdf7], |
| 164 | [0x54b8e14, '49769333513942933689', 20, 0xc6d1b5f2], |
| 165 | [0xd6aa5616, '54979784887993251199', 20, 0x32476461], |
| 166 | [0x11e63098, '58360544869206793220', 20, 0xd917cf1a], |
| 167 | [0xbe92385, '27347953487840714234', 20, 0x4ad14a12], |
| 168 | [0x49511de0, '07650690295365319082', 20, 0xe37b5c6c], |
| 169 | [0x3db13bc1, '42655507906821911703', 20, 0x7cc497f1], |
| 170 | [0xbb899bea, '29977409200786225655', 20, 0x99781bb2], |
| 171 | [0xf6cd9436, '85181542907229116674', 20, 0x132256a1], |
| 172 | [0x9109e6c3, '87963594337989416799', 20, 0xbfdb2c83], |
| 173 | [0x75770fc, '21395988329504168551', 20, 0x8d9d1e81], |
| 174 | [0x69b1d19b, '51991013580943379423', 20, 0x7b6d4404], |
| 175 | [0xc6132975, '*]+@!);({_$;}[_},?{?;(_?,=-][@', 30, 0x8619f010], |
| 176 | [0xd58cb00c, '_@:_).&(#.[:[{[:)$++-($_;@[)}+', 30, 0x15746ac3], |
| 177 | [0xb63b8caa, '&[!,[$_==}+.]@!;*(+},[;:)$;)-@', 30, 0xaccf812f], |
| 178 | [0x8a45a2b8, ']{.[.+?+[[=;[?}_#&;[=)__$$:+=_', 30, 0x78af45de], |
| 179 | [0xcbe95b78, '-%.)=/[@].:.(:,()$;=%@-$?]{%+%', 30, 0x25b06b59], |
| 180 | [0x4ef8a54b, '+]#$(@&.=:,*];/.!]%/{:){:@(;)$', 30, 0x4ba0d08f], |
| 181 | [0x76ad267a, ')-._.:?[&:.=+}(*$/=!.${;(=$@!}', 30, 0xe26b6aac], |
| 182 | [0x569e613c, ':(_*&%/[[}+,?#$&*+#[([*-/#;%(]', 30, 0x7e2b0a66], |
| 183 | [0x36aa61da, '{[#-;:$/{)(+[}#]/{&!%(@)%:@-$:', 30, 0xb3430dc7], |
| 184 | [0xf67222df, '_{$*,}(&,@.)):=!/%(&(,,-?$}}}!', 30, 0x626c17a], |
| 185 | [ |
| 186 | 0x74b34fd3, |
| 187 | 'e$98KNzqaV)Y:2X?]77].{gKRD4G5{mHZk,Z)SpU%L3FSgv!Wb8MLAFdi{+fp)c,@8m6v)yXg@]HBDFk?.4&}g5_udE*JHCiH=aL', |
| 188 | 100, |
| 189 | 0xccf98060, |
| 190 | ], |
| 191 | [ |
| 192 | 0x351fd770, |
| 193 | 'r*Fd}ef+5RJQ;+W=4jTR9)R*p!B;]Ed7tkrLi;88U7g@3v!5pk2X6D)vt,.@N8c]@yyEcKi[vwUu@.Ppm@C6%Mv*3Nw}Y,58_aH)', |
| 194 | 100, |
| 195 | 0xd8b95312, |
| 196 | ], |
| 197 | [ |
| 198 | 0xc45aef77, |
| 199 | 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&', |
| 200 | 100, |
| 201 | 0xbb1c9912, |
| 202 | ], |
| 203 | [ |
| 204 | 0xc45aef77, |
| 205 | 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&' + |
| 206 | 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&' + |
| 207 | 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&' + |
| 208 | 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&' + |
| 209 | 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&' + |
| 210 | 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&', |
| 211 | 600, |
| 212 | 0x888afa5b, |
| 213 | ], |
| 214 | ]; |
| 215 | |
| 216 | for (const [crc, data, len, expected] of tests) { |
| 217 | if (data === 0) { |
| 218 | continue; |
| 219 | } |
| 220 | const buf = Buffer.from(data, 'utf8'); |
| 221 | assert.strictEqual(buf.length, len); |
| 222 | assert.strictEqual( |
| 223 | zlib.crc32(buf, crc), |
| 224 | expected, |
| 225 | `crc32('${data}', ${crc}) in buffer is not ${expected}` |
| 226 | ); |
| 227 | assert.strictEqual( |
| 228 | zlib.crc32(buf.toString(), crc), |
| 229 | expected, |
| 230 | `crc32('${data}', ${crc}) in string is not ${expected}` |
| 231 | ); |
| 232 | if (crc === 0) { |
| 233 | assert.strictEqual( |
| 234 | zlib.crc32(buf), |
| 235 | expected, |
| 236 | `crc32('${data}') in buffer is not ${expected}` |
| 237 | ); |
| 238 | assert.strictEqual( |
| 239 | zlib.crc32(buf.toString()), |
| 240 | expected, |
| 241 | `crc32('${data}') in string is not ${expected}` |
| 242 | ); |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | for (const invalid of [undefined, null, true, 1, () => {}, {}]) { |
| 247 | assert.throws(() => { |
| 248 | zlib.crc32(invalid); |
| 249 | }, new TypeError("Failed to execute 'crc32' on 'ZlibUtil': parameter 1 is not of type 'string or ArrayBuffer or ArrayBufferView'.")); |
| 250 | } |
| 251 | |
| 252 | for (const invalid of [null, true, () => {}, {}]) { |
| 253 | assert.throws( |
| 254 | () => { |
| 255 | zlib.crc32('test', invalid); |
| 256 | }, |
| 257 | { code: 'ERR_INVALID_ARG_TYPE' } |
| 258 | ); |
| 259 | } |
| 260 | }, |
| 261 | }; |
| 262 | |
| 263 | export const constantsTest = { |
| 264 | test() { |
| 265 | assert.deepStrictEqual(Object.keys(zlib.constants).sort(), [ |
| 266 | 'BROTLI_DECODE', |
| 267 | 'BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES', |
| 268 | 'BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP', |
| 269 | 'BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES', |
| 270 | 'BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1', |
| 271 | 'BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2', |
| 272 | 'BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS', |
| 273 | 'BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET', |
| 274 | 'BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1', |
| 275 | 'BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2', |
| 276 | 'BROTLI_DECODER_ERROR_FORMAT_CL_SPACE', |
| 277 | 'BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT', |
| 278 | 'BROTLI_DECODER_ERROR_FORMAT_DICTIONARY', |
| 279 | 'BROTLI_DECODER_ERROR_FORMAT_DISTANCE', |
| 280 | 'BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE', |
| 281 | 'BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE', |
| 282 | 'BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE', |
| 283 | 'BROTLI_DECODER_ERROR_FORMAT_PADDING_1', |
| 284 | 'BROTLI_DECODER_ERROR_FORMAT_PADDING_2', |
| 285 | 'BROTLI_DECODER_ERROR_FORMAT_RESERVED', |
| 286 | 'BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET', |
| 287 | 'BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME', |
| 288 | 'BROTLI_DECODER_ERROR_FORMAT_TRANSFORM', |
| 289 | 'BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS', |
| 290 | 'BROTLI_DECODER_ERROR_INVALID_ARGUMENTS', |
| 291 | 'BROTLI_DECODER_ERROR_UNREACHABLE', |
| 292 | 'BROTLI_DECODER_NEEDS_MORE_INPUT', |
| 293 | 'BROTLI_DECODER_NEEDS_MORE_OUTPUT', |
| 294 | 'BROTLI_DECODER_NO_ERROR', |
| 295 | 'BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION', |
| 296 | 'BROTLI_DECODER_PARAM_LARGE_WINDOW', |
| 297 | 'BROTLI_DECODER_RESULT_ERROR', |
| 298 | 'BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT', |
| 299 | 'BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT', |
| 300 | 'BROTLI_DECODER_RESULT_SUCCESS', |
| 301 | 'BROTLI_DECODER_SUCCESS', |
| 302 | 'BROTLI_DEFAULT_MODE', |
| 303 | 'BROTLI_DEFAULT_QUALITY', |
| 304 | 'BROTLI_DEFAULT_WINDOW', |
| 305 | 'BROTLI_ENCODE', |
| 306 | 'BROTLI_LARGE_MAX_WINDOW_BITS', |
| 307 | 'BROTLI_MAX_INPUT_BLOCK_BITS', |
| 308 | 'BROTLI_MAX_QUALITY', |
| 309 | 'BROTLI_MAX_WINDOW_BITS', |
| 310 | 'BROTLI_MIN_INPUT_BLOCK_BITS', |
| 311 | 'BROTLI_MIN_QUALITY', |
| 312 | 'BROTLI_MIN_WINDOW_BITS', |
| 313 | 'BROTLI_MODE_FONT', |
| 314 | 'BROTLI_MODE_GENERIC', |
| 315 | 'BROTLI_MODE_TEXT', |
| 316 | 'BROTLI_OPERATION_EMIT_METADATA', |
| 317 | 'BROTLI_OPERATION_FINISH', |
| 318 | 'BROTLI_OPERATION_FLUSH', |
| 319 | 'BROTLI_OPERATION_PROCESS', |
| 320 | 'BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING', |
| 321 | 'BROTLI_PARAM_LARGE_WINDOW', |
| 322 | 'BROTLI_PARAM_LGBLOCK', |
| 323 | 'BROTLI_PARAM_LGWIN', |
| 324 | 'BROTLI_PARAM_MODE', |
| 325 | 'BROTLI_PARAM_NDIRECT', |
| 326 | 'BROTLI_PARAM_NPOSTFIX', |
| 327 | 'BROTLI_PARAM_QUALITY', |
| 328 | 'BROTLI_PARAM_SIZE_HINT', |
| 329 | 'DEFLATE', |
| 330 | 'DEFLATERAW', |
| 331 | 'GUNZIP', |
| 332 | 'GZIP', |
| 333 | 'INFLATE', |
| 334 | 'INFLATERAW', |
| 335 | 'UNZIP', |
| 336 | 'ZLIB_VERNUM', |
| 337 | 'ZSTD_CLEVEL_DEFAULT', |
| 338 | 'ZSTD_COMPRESS', |
| 339 | 'ZSTD_DECODE', |
| 340 | 'ZSTD_DECOMPRESS', |
| 341 | 'ZSTD_ENCODE', |
| 342 | 'ZSTD_btlazy2', |
| 343 | 'ZSTD_btopt', |
| 344 | 'ZSTD_btultra', |
| 345 | 'ZSTD_btultra2', |
| 346 | 'ZSTD_c_chainLog', |
| 347 | 'ZSTD_c_checksumFlag', |
| 348 | 'ZSTD_c_compressionLevel', |
| 349 | 'ZSTD_c_contentSizeFlag', |
| 350 | 'ZSTD_c_dictIDFlag', |
| 351 | 'ZSTD_c_enableLongDistanceMatching', |
| 352 | 'ZSTD_c_hashLog', |
| 353 | 'ZSTD_c_jobSize', |
| 354 | 'ZSTD_c_ldmBucketSizeLog', |
| 355 | 'ZSTD_c_ldmHashLog', |
| 356 | 'ZSTD_c_ldmHashRateLog', |
| 357 | 'ZSTD_c_ldmMinMatch', |
| 358 | 'ZSTD_c_minMatch', |
| 359 | 'ZSTD_c_nbWorkers', |
| 360 | 'ZSTD_c_overlapLog', |
| 361 | 'ZSTD_c_searchLog', |
| 362 | 'ZSTD_c_strategy', |
| 363 | 'ZSTD_c_targetLength', |
| 364 | 'ZSTD_c_windowLog', |
| 365 | 'ZSTD_d_windowLogMax', |
| 366 | 'ZSTD_dfast', |
| 367 | 'ZSTD_e_continue', |
| 368 | 'ZSTD_e_end', |
| 369 | 'ZSTD_e_flush', |
| 370 | 'ZSTD_error_GENERIC', |
| 371 | 'ZSTD_error_checksum_wrong', |
| 372 | 'ZSTD_error_corruption_detected', |
| 373 | 'ZSTD_error_dictionaryCreation_failed', |
| 374 | 'ZSTD_error_dictionary_corrupted', |
| 375 | 'ZSTD_error_dictionary_wrong', |
| 376 | 'ZSTD_error_dstBuffer_null', |
| 377 | 'ZSTD_error_dstSize_tooSmall', |
| 378 | 'ZSTD_error_frameParameter_unsupported', |
| 379 | 'ZSTD_error_frameParameter_windowTooLarge', |
| 380 | 'ZSTD_error_init_missing', |
| 381 | 'ZSTD_error_literals_headerWrong', |
| 382 | 'ZSTD_error_maxSymbolValue_tooLarge', |
| 383 | 'ZSTD_error_maxSymbolValue_tooSmall', |
| 384 | 'ZSTD_error_memory_allocation', |
| 385 | 'ZSTD_error_noForwardProgress_destFull', |
| 386 | 'ZSTD_error_noForwardProgress_inputEmpty', |
| 387 | 'ZSTD_error_no_error', |
| 388 | 'ZSTD_error_parameter_combination_unsupported', |
| 389 | 'ZSTD_error_parameter_outOfBound', |
| 390 | 'ZSTD_error_parameter_unsupported', |
| 391 | 'ZSTD_error_prefix_unknown', |
| 392 | 'ZSTD_error_srcSize_wrong', |
| 393 | 'ZSTD_error_stabilityCondition_notRespected', |
| 394 | 'ZSTD_error_stage_wrong', |
| 395 | 'ZSTD_error_tableLog_tooLarge', |
| 396 | 'ZSTD_error_version_unsupported', |
| 397 | 'ZSTD_error_workSpace_tooSmall', |
| 398 | 'ZSTD_fast', |
| 399 | 'ZSTD_greedy', |
| 400 | 'ZSTD_lazy', |
| 401 | 'ZSTD_lazy2', |
| 402 | 'Z_BEST_COMPRESSION', |
| 403 | 'Z_BEST_SPEED', |
| 404 | 'Z_BLOCK', |
| 405 | 'Z_BUF_ERROR', |
| 406 | 'Z_DATA_ERROR', |
| 407 | 'Z_DEFAULT_CHUNK', |
| 408 | 'Z_DEFAULT_COMPRESSION', |
| 409 | 'Z_DEFAULT_LEVEL', |
| 410 | 'Z_DEFAULT_MEMLEVEL', |
| 411 | 'Z_DEFAULT_STRATEGY', |
| 412 | 'Z_DEFAULT_WINDOWBITS', |
| 413 | 'Z_ERRNO', |
| 414 | 'Z_FILTERED', |
| 415 | 'Z_FINISH', |
| 416 | 'Z_FIXED', |
| 417 | 'Z_FULL_FLUSH', |
| 418 | 'Z_HUFFMAN_ONLY', |
| 419 | 'Z_MAX_CHUNK', |
| 420 | 'Z_MAX_LEVEL', |
| 421 | 'Z_MAX_MEMLEVEL', |
| 422 | 'Z_MAX_WINDOWBITS', |
| 423 | 'Z_MEM_ERROR', |
| 424 | 'Z_MIN_CHUNK', |
| 425 | 'Z_MIN_LEVEL', |
| 426 | 'Z_MIN_MEMLEVEL', |
| 427 | 'Z_MIN_WINDOWBITS', |
| 428 | 'Z_NEED_DICT', |
| 429 | 'Z_NO_COMPRESSION', |
| 430 | 'Z_NO_FLUSH', |
| 431 | 'Z_OK', |
| 432 | 'Z_PARTIAL_FLUSH', |
| 433 | 'Z_RLE', |
| 434 | 'Z_STREAM_END', |
| 435 | 'Z_STREAM_ERROR', |
| 436 | 'Z_SYNC_FLUSH', |
| 437 | 'Z_VERSION_ERROR', |
| 438 | ]); |
| 439 | }, |
| 440 | }; |
| 441 | |
| 442 | // This test makes sure constants can be accessed from the zlib module directly |
| 443 | // This is supported by NodeJS, but is deprecated. |
| 444 | export const constantsDirectlyUnderZlibTest = { |
| 445 | test() { |
| 446 | assert.deepStrictEqual(zlib.Z_NEED_DICT, 2); |
| 447 | assert.deepStrictEqual(zlib.Z_OK, 0); |
| 448 | }, |
| 449 | }; |
| 450 | |
| 451 | // Tests are taken from: |
| 452 | // https://github.com/nodejs/node/blob/561bc87c7607208f0d3db6dcd9231efeb48cfe2f/test/parallel/test-zlib-zero-windowBits.js |
| 453 | export const testZeroWindowBits = { |
| 454 | test() { |
| 455 | // windowBits is a special case in zlib. On the compression side, 0 is invalid. |
| 456 | // On the decompression side, it indicates that zlib should use the value from |
| 457 | // the header of the compressed stream. |
| 458 | { |
| 459 | const inflate = zlib.createInflate({ windowBits: 0 }); |
| 460 | assert(inflate instanceof zlib.Inflate); |
| 461 | } |
| 462 | |
| 463 | { |
| 464 | const gunzip = zlib.createGunzip({ windowBits: 0 }); |
| 465 | assert(gunzip instanceof zlib.Gunzip); |
| 466 | } |
| 467 | |
| 468 | { |
| 469 | const unzip = zlib.createUnzip({ windowBits: 0 }); |
| 470 | assert(unzip instanceof zlib.Unzip); |
| 471 | } |
| 472 | |
| 473 | assert.throws(() => zlib.createGzip({ windowBits: 0 }), { |
| 474 | code: 'ERR_OUT_OF_RANGE', |
| 475 | name: 'RangeError', |
| 476 | message: |
| 477 | 'The value of "options.windowBits" is out of range. ' + |
| 478 | 'It must be >= 9 and <= 15. Received 0', |
| 479 | }); |
| 480 | }, |
| 481 | }; |
| 482 | |
| 483 | // Tests are taken from: |
| 484 | // https://github.com/nodejs/node/blob/561bc87c7607208f0d3db6dcd9231efeb48cfe2f/test/parallel/test-zlib-create-raw.js |
| 485 | export const testCreateRaw = { |
| 486 | test() { |
| 487 | { |
| 488 | const inflateRaw = zlib.createInflateRaw(); |
| 489 | assert(inflateRaw instanceof zlib.InflateRaw); |
| 490 | } |
| 491 | |
| 492 | { |
| 493 | const deflateRaw = zlib.createDeflateRaw(); |
| 494 | assert(deflateRaw instanceof zlib.DeflateRaw); |
| 495 | } |
| 496 | }, |
| 497 | }; |
| 498 | |
| 499 | // Tests are taken from: |
| 500 | // https://github.com/nodejs/node/blob/561bc87c7607208f0d3db6dcd9231efeb48cfe2f/test/parallel/test-zlib-deflate-constructors.js |
| 501 | export const testDeflateConstructors = { |
| 502 | test() { |
| 503 | assert(new zlib.Deflate() instanceof zlib.Deflate); |
| 504 | assert(new zlib.DeflateRaw() instanceof zlib.DeflateRaw); |
| 505 | |
| 506 | // Throws if `options.chunkSize` is invalid |
| 507 | assert.throws(() => new zlib.Deflate({ chunkSize: 'test' }), { |
| 508 | code: 'ERR_INVALID_ARG_TYPE', |
| 509 | name: 'TypeError', |
| 510 | }); |
| 511 | |
| 512 | assert.throws(() => new zlib.Deflate({ chunkSize: -Infinity }), { |
| 513 | code: 'ERR_OUT_OF_RANGE', |
| 514 | name: 'RangeError', |
| 515 | }); |
| 516 | |
| 517 | assert.throws(() => new zlib.Deflate({ chunkSize: 0 }), { |
| 518 | code: 'ERR_OUT_OF_RANGE', |
| 519 | name: 'RangeError', |
| 520 | }); |
| 521 | |
| 522 | // Throws if `options.windowBits` is invalid |
| 523 | assert.throws(() => new zlib.Deflate({ windowBits: 'test' }), { |
| 524 | code: 'ERR_INVALID_ARG_TYPE', |
| 525 | name: 'TypeError', |
| 526 | }); |
| 527 | |
| 528 | assert.throws(() => new zlib.Deflate({ windowBits: -Infinity }), { |
| 529 | code: 'ERR_OUT_OF_RANGE', |
| 530 | name: 'RangeError', |
| 531 | }); |
| 532 | |
| 533 | assert.throws(() => new zlib.Deflate({ windowBits: Infinity }), { |
| 534 | code: 'ERR_OUT_OF_RANGE', |
| 535 | name: 'RangeError', |
| 536 | }); |
| 537 | |
| 538 | assert.throws(() => new zlib.Deflate({ windowBits: 0 }), { |
| 539 | code: 'ERR_OUT_OF_RANGE', |
| 540 | name: 'RangeError', |
| 541 | }); |
| 542 | |
| 543 | // Throws if `options.level` is invalid |
| 544 | assert.throws(() => new zlib.Deflate({ level: 'test' }), { |
| 545 | code: 'ERR_INVALID_ARG_TYPE', |
| 546 | name: 'TypeError', |
| 547 | }); |
| 548 | |
| 549 | assert.throws(() => new zlib.Deflate({ level: -Infinity }), { |
| 550 | code: 'ERR_OUT_OF_RANGE', |
| 551 | name: 'RangeError', |
| 552 | }); |
| 553 | |
| 554 | assert.throws(() => new zlib.Deflate({ level: Infinity }), { |
| 555 | code: 'ERR_OUT_OF_RANGE', |
| 556 | name: 'RangeError', |
| 557 | }); |
| 558 | |
| 559 | assert.throws(() => new zlib.Deflate({ level: -2 }), { |
| 560 | code: 'ERR_OUT_OF_RANGE', |
| 561 | name: 'RangeError', |
| 562 | }); |
| 563 | |
| 564 | // Throws if `level` invalid in `Deflate.prototype.params()` |
| 565 | assert.throws(() => new zlib.Deflate().params('test'), { |
| 566 | code: 'ERR_INVALID_ARG_TYPE', |
| 567 | name: 'TypeError', |
| 568 | }); |
| 569 | |
| 570 | assert.throws(() => new zlib.Deflate().params(-Infinity), { |
| 571 | code: 'ERR_OUT_OF_RANGE', |
| 572 | name: 'RangeError', |
| 573 | }); |
| 574 | |
| 575 | assert.throws(() => new zlib.Deflate().params(Infinity), { |
| 576 | code: 'ERR_OUT_OF_RANGE', |
| 577 | name: 'RangeError', |
| 578 | }); |
| 579 | |
| 580 | assert.throws(() => new zlib.Deflate().params(-2), { |
| 581 | code: 'ERR_OUT_OF_RANGE', |
| 582 | name: 'RangeError', |
| 583 | }); |
| 584 | |
| 585 | // Throws if options.memLevel is invalid |
| 586 | assert.throws(() => new zlib.Deflate({ memLevel: 'test' }), { |
| 587 | code: 'ERR_INVALID_ARG_TYPE', |
| 588 | name: 'TypeError', |
| 589 | }); |
| 590 | |
| 591 | assert.throws(() => new zlib.Deflate({ memLevel: -Infinity }), { |
| 592 | code: 'ERR_OUT_OF_RANGE', |
| 593 | name: 'RangeError', |
| 594 | }); |
| 595 | |
| 596 | assert.throws(() => new zlib.Deflate({ memLevel: Infinity }), { |
| 597 | code: 'ERR_OUT_OF_RANGE', |
| 598 | name: 'RangeError', |
| 599 | }); |
| 600 | |
| 601 | assert.throws(() => new zlib.Deflate({ memLevel: -2 }), { |
| 602 | code: 'ERR_OUT_OF_RANGE', |
| 603 | name: 'RangeError', |
| 604 | }); |
| 605 | |
| 606 | // Does not throw if opts.strategy is valid |
| 607 | new zlib.Deflate({ strategy: zlib.constants.Z_FILTERED }); |
| 608 | new zlib.Deflate({ strategy: zlib.constants.Z_HUFFMAN_ONLY }); |
| 609 | new zlib.Deflate({ strategy: zlib.constants.Z_RLE }); |
| 610 | new zlib.Deflate({ strategy: zlib.constants.Z_FIXED }); |
| 611 | new zlib.Deflate({ strategy: zlib.constants.Z_DEFAULT_STRATEGY }); |
| 612 | |
| 613 | // Throws if options.strategy is invalid |
| 614 | assert.throws(() => new zlib.Deflate({ strategy: 'test' }), { |
| 615 | code: 'ERR_INVALID_ARG_TYPE', |
| 616 | name: 'TypeError', |
| 617 | }); |
| 618 | |
| 619 | assert.throws(() => new zlib.Deflate({ strategy: -Infinity }), { |
| 620 | code: 'ERR_OUT_OF_RANGE', |
| 621 | name: 'RangeError', |
| 622 | }); |
| 623 | |
| 624 | assert.throws(() => new zlib.Deflate({ strategy: Infinity }), { |
| 625 | code: 'ERR_OUT_OF_RANGE', |
| 626 | name: 'RangeError', |
| 627 | }); |
| 628 | |
| 629 | assert.throws(() => new zlib.Deflate({ strategy: -2 }), { |
| 630 | code: 'ERR_OUT_OF_RANGE', |
| 631 | name: 'RangeError', |
| 632 | }); |
| 633 | |
| 634 | // Throws TypeError if `strategy` is invalid in `Deflate.prototype.params()` |
| 635 | assert.throws(() => new zlib.Deflate().params(0, 'test'), { |
| 636 | code: 'ERR_INVALID_ARG_TYPE', |
| 637 | name: 'TypeError', |
| 638 | }); |
| 639 | |
| 640 | assert.throws(() => new zlib.Deflate().params(0, -Infinity), { |
| 641 | code: 'ERR_OUT_OF_RANGE', |
| 642 | name: 'RangeError', |
| 643 | }); |
| 644 | |
| 645 | assert.throws(() => new zlib.Deflate().params(0, Infinity), { |
| 646 | code: 'ERR_OUT_OF_RANGE', |
| 647 | name: 'RangeError', |
| 648 | }); |
| 649 | |
| 650 | assert.throws(() => new zlib.Deflate().params(0, -2), { |
| 651 | code: 'ERR_OUT_OF_RANGE', |
| 652 | name: 'RangeError', |
| 653 | }); |
| 654 | |
| 655 | // Throws if opts.dictionary is not a Buffer |
| 656 | assert.throws(() => new zlib.Deflate({ dictionary: 'not a buffer' }), { |
| 657 | code: 'ERR_INVALID_ARG_TYPE', |
| 658 | name: 'TypeError', |
| 659 | }); |
| 660 | }, |
| 661 | }; |
| 662 | |
| 663 | // Tests are taken from: |
| 664 | // https://github.com/nodejs/node/blob/561bc87c7607208f0d3db6dcd9231efeb48cfe2f/test/parallel/test-zlib-failed-init.js |
| 665 | export const testFailedInit = { |
| 666 | test() { |
| 667 | assert.throws(() => zlib.createGzip({ chunkSize: 0 }), { |
| 668 | code: 'ERR_OUT_OF_RANGE', |
| 669 | name: 'RangeError', |
| 670 | }); |
| 671 | |
| 672 | assert.throws(() => zlib.createGzip({ windowBits: 0 }), { |
| 673 | code: 'ERR_OUT_OF_RANGE', |
| 674 | name: 'RangeError', |
| 675 | }); |
| 676 | |
| 677 | assert.throws(() => zlib.createGzip({ memLevel: 0 }), { |
| 678 | code: 'ERR_OUT_OF_RANGE', |
| 679 | name: 'RangeError', |
| 680 | }); |
| 681 | |
| 682 | { |
| 683 | const stream = zlib.createGzip({ level: NaN }); |
| 684 | assert.strictEqual(stream._level, zlib.constants.Z_DEFAULT_COMPRESSION); |
| 685 | } |
| 686 | |
| 687 | { |
| 688 | const stream = zlib.createGzip({ strategy: NaN }); |
| 689 | assert.strictEqual(stream._strategy, zlib.constants.Z_DEFAULT_STRATEGY); |
| 690 | } |
| 691 | }, |
| 692 | }; |
| 693 | |
| 694 | // Tests are taken from: |
| 695 | // https://github.com/nodejs/node/blob/561bc87c7607208f0d3db6dcd9231efeb48cfe2f/test/parallel/test-zlib-destroy.js |
| 696 | export const zlibDestroyTest = { |
| 697 | async test() { |
| 698 | const promises = []; |
| 699 | // Verify that the zlib transform does clean up |
| 700 | // the handle when calling destroy. |
| 701 | { |
| 702 | const ts = zlib.createGzip(); |
| 703 | ts.destroy(); |
| 704 | assert.strictEqual(ts._handle, null); |
| 705 | |
| 706 | const { promise, resolve, reject } = Promise.withResolvers(); |
| 707 | promises.push(promise); |
| 708 | ts.on('error', reject); |
| 709 | ts.on('close', () => { |
| 710 | ts.close(() => { |
| 711 | resolve(); |
| 712 | }); |
| 713 | }); |
| 714 | } |
| 715 | |
| 716 | { |
| 717 | // Ensure 'error' is only emitted once. |
| 718 | const decompress = zlib.createGunzip(15); |
| 719 | const { promise, resolve, reject: _reject } = Promise.withResolvers(); |
| 720 | promises.push(promise); |
| 721 | let errorCount = 0; |
| 722 | decompress.on('error', (err) => { |
| 723 | errorCount++; |
| 724 | decompress.close(); |
| 725 | assert.strictEqual(errorCount, 1, 'Error should only be emitted once'); |
| 726 | resolve(); |
| 727 | }); |
| 728 | |
| 729 | decompress.write('something invalid'); |
| 730 | decompress.destroy(new Error('asd')); |
| 731 | } |
| 732 | |
| 733 | await Promise.all(promises); |
| 734 | }, |
| 735 | }; |
| 736 | |
| 737 | // Tests are taken from: |
| 738 | // https://github.com/nodejs/node/blob/561bc87c7607208f0d3db6dcd9231efeb48cfe2f/test/parallel/test-zlib-close-after-error.js |
| 739 | export const closeAfterError = { |
| 740 | async test() { |
| 741 | const decompress = zlib.createGunzip(15); |
| 742 | const { promise, resolve } = Promise.withResolvers(); |
| 743 | let errorHasBeenCalled = false; |
| 744 | |
| 745 | decompress.on('error', () => { |
| 746 | errorHasBeenCalled = true; |
| 747 | assert.strictEqual(decompress._closed, true); |
| 748 | decompress.close(); |
| 749 | }); |
| 750 | |
| 751 | assert.strictEqual(decompress._closed, false); |
| 752 | decompress.write('something invalid'); |
| 753 | decompress.on('close', resolve); |
| 754 | |
| 755 | await promise; |
| 756 | |
| 757 | assert(errorHasBeenCalled, 'Error handler should have been called'); |
| 758 | }, |
| 759 | }; |
| 760 | |
| 761 | // Tests are taken from: |
| 762 | // https://github.com/nodejs/node/blob/561bc87c7607208f0d3db6dcd9231efeb48cfe2f/test/parallel/test-zlib-write-after-close.js |
| 763 | export const writeAfterClose = { |
| 764 | async test() { |
| 765 | const { promise, resolve } = Promise.withResolvers(); |
| 766 | let close = mock.fn(); |
| 767 | zlib.gzip('hello', function (err, out) { |
| 768 | const unzip = zlib.createGunzip(); |
| 769 | unzip.close(close); |
| 770 | unzip.write('asd', (err) => { |
| 771 | assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED'); |
| 772 | assert.strictEqual(err.name, 'Error'); |
| 773 | resolve(); |
| 774 | }); |
| 775 | }); |
| 776 | await promise; |
| 777 | assert.strictEqual(close.mock.callCount(), 1); |
| 778 | }, |
| 779 | }; |
| 780 | |
| 781 | // Tests are taken from: |
| 782 | // https://github.com/nodejs/node/blob/9edf4a0856681a7665bd9dcf2ca7cac252784b98/test/parallel/test-zlib-bytes-read.js |
| 783 | export const testZlibBytesRead = { |
| 784 | async test() { |
| 785 | const expectStr = 'abcdefghijklmnopqrstuvwxyz'.repeat(2); |
| 786 | const expectBuf = Buffer.from(expectStr); |
| 787 | |
| 788 | function createWriter(target, buffer) { |
| 789 | const writer = { size: 0 }; |
| 790 | const write = () => { |
| 791 | target.write(Buffer.from([buffer[writer.size++]]), () => { |
| 792 | if (writer.size < buffer.length) { |
| 793 | target.flush(write); |
| 794 | } else { |
| 795 | target.end(); |
| 796 | } |
| 797 | }); |
| 798 | }; |
| 799 | write(); |
| 800 | return writer; |
| 801 | } |
| 802 | |
| 803 | // This test is simplified a lot because of test runner limitations. |
| 804 | for (const method of [ |
| 805 | 'createGzip', |
| 806 | 'createDeflate', |
| 807 | 'createDeflateRaw', |
| 808 | 'createBrotliCompress', |
| 809 | ]) { |
| 810 | assert(method in zlib, `${method} is not available in "node:zlib"`); |
| 811 | const { promise, resolve, reject } = Promise.withResolvers(); |
| 812 | let compData = Buffer.alloc(0); |
| 813 | const comp = zlib[method](); |
| 814 | const compWriter = createWriter(comp, expectBuf); |
| 815 | comp.on('data', function (d) { |
| 816 | compData = Buffer.concat([compData, d]); |
| 817 | assert.strictEqual( |
| 818 | this.bytesWritten, |
| 819 | compWriter.size, |
| 820 | `Should get write size on ${method} data.` |
| 821 | ); |
| 822 | }); |
| 823 | comp.on('error', reject); |
| 824 | comp.on('end', function () { |
| 825 | assert.strictEqual( |
| 826 | this.bytesWritten, |
| 827 | compWriter.size, |
| 828 | `Should get write size on ${method} end.` |
| 829 | ); |
| 830 | assert.strictEqual( |
| 831 | this.bytesWritten, |
| 832 | expectStr.length, |
| 833 | `Should get data size on ${method} end.` |
| 834 | ); |
| 835 | |
| 836 | resolve(); |
| 837 | }); |
| 838 | |
| 839 | await promise; |
| 840 | } |
| 841 | }, |
| 842 | }; |
| 843 | |
| 844 | // Tests are taken from: |
| 845 | // https://github.com/nodejs/node/blob/3a71ccf6c473357e89be61b26739fd9139dce4db/test/parallel/test-zlib-const.js |
| 846 | export const zlibConst = { |
| 847 | test() { |
| 848 | assert.strictEqual(zlib.constants.Z_OK, 0, 'Expected Z_OK to be 0'); |
| 849 | assert.throws(() => { |
| 850 | zlib.constants.Z_OK = 1; |
| 851 | }, /Cannot assign to read only property/); |
| 852 | assert.strictEqual(zlib.constants.Z_OK, 0, 'Z_OK should be immutable'); |
| 853 | assert.strictEqual( |
| 854 | zlib.codes.Z_OK, |
| 855 | 0, |
| 856 | `Expected Z_OK to be 0; got ${zlib.codes.Z_OK}` |
| 857 | ); |
| 858 | assert.throws(() => { |
| 859 | zlib.codes.Z_OK = 1; |
| 860 | }, /Cannot assign to read only property/); |
| 861 | assert.strictEqual(zlib.codes.Z_OK, 0, 'Z_OK should be immutable'); |
| 862 | assert(Object.isFrozen(zlib.codes), 'Expected zlib.codes to be frozen'); |
| 863 | |
| 864 | assert.deepStrictEqual(zlib.codes, { |
| 865 | '-1': 'Z_ERRNO', |
| 866 | '-2': 'Z_STREAM_ERROR', |
| 867 | '-3': 'Z_DATA_ERROR', |
| 868 | '-4': 'Z_MEM_ERROR', |
| 869 | '-5': 'Z_BUF_ERROR', |
| 870 | '-6': 'Z_VERSION_ERROR', |
| 871 | 0: 'Z_OK', |
| 872 | 1: 'Z_STREAM_END', |
| 873 | 2: 'Z_NEED_DICT', |
| 874 | Z_BUF_ERROR: -5, |
| 875 | Z_DATA_ERROR: -3, |
| 876 | Z_ERRNO: -1, |
| 877 | Z_MEM_ERROR: -4, |
| 878 | Z_NEED_DICT: 2, |
| 879 | Z_OK: 0, |
| 880 | Z_STREAM_END: 1, |
| 881 | Z_STREAM_ERROR: -2, |
| 882 | Z_VERSION_ERROR: -6, |
| 883 | }); |
| 884 | }, |
| 885 | }; |
| 886 | |
| 887 | // Tests are taken from: |
| 888 | // https://github.com/nodejs/node/blob/3a71ccf6c473357e89be61b26739fd9139dce4db/test/parallel/test-zlib-object-write.js |
| 889 | |
| 890 | export const zlibObjectWrite = { |
| 891 | async test() { |
| 892 | const { promise, resolve, reject } = Promise.withResolvers(); |
| 893 | const gunzip = new zlib.Gunzip({ objectMode: true }); |
| 894 | gunzip.on('error', reject); |
| 895 | assert.throws( |
| 896 | () => { |
| 897 | gunzip.write({}); |
| 898 | }, |
| 899 | { |
| 900 | name: 'TypeError', |
| 901 | code: 'ERR_INVALID_ARG_TYPE', |
| 902 | } |
| 903 | ); |
| 904 | gunzip.on('close', resolve); |
| 905 | gunzip.close(); |
| 906 | await promise; |
| 907 | }, |
| 908 | }; |
| 909 | |
| 910 | // Tests are taken from: |
| 911 | // https://github.com/nodejs/node/blob/3a71ccf6c473357e89be61b26739fd9139dce4db/test/parallel/test-zlib-zero-byte.js |
| 912 | export const zlibZeroByte = { |
| 913 | async test() { |
| 914 | for (const Compressor of [zlib.Gzip, zlib.BrotliCompress]) { |
| 915 | const { promise, resolve, reject } = Promise.withResolvers(); |
| 916 | let endCalled = false; |
| 917 | const gz = new Compressor(); |
| 918 | const emptyBuffer = Buffer.alloc(0); |
| 919 | let received = 0; |
| 920 | gz.on('data', function (c) { |
| 921 | received += c.length; |
| 922 | }); |
| 923 | |
| 924 | gz.on('end', function () { |
| 925 | const expected = Compressor === zlib.Gzip ? 20 : 1; |
| 926 | assert.strictEqual( |
| 927 | received, |
| 928 | expected, |
| 929 | `${received}, ${expected}, ${Compressor.name}` |
| 930 | ); |
| 931 | endCalled = true; |
| 932 | }); |
| 933 | gz.on('error', reject); |
| 934 | gz.on('finish', resolve); |
| 935 | gz.write(emptyBuffer); |
| 936 | gz.end(); |
| 937 | |
| 938 | await promise; |
| 939 | assert(endCalled, 'End should have been called'); |
| 940 | } |
| 941 | }, |
| 942 | }; |
| 943 | |
| 944 | // Tests are taken from: |
| 945 | // https://github.com/nodejs/node/blob/431ac161e65698152de197703fb30c89da2b6686/test/parallel/test-zlib-dictionary-fail.js |
| 946 | export const zlibDictionaryFail = { |
| 947 | async test() { |
| 948 | // String "test" encoded with dictionary "dict". |
| 949 | const input = Buffer.from([0x78, 0xbb, 0x04, 0x09, 0x01, 0xa5]); |
| 950 | |
| 951 | { |
| 952 | const { promise, resolve } = Promise.withResolvers(); |
| 953 | const stream = zlib.createInflate(); |
| 954 | |
| 955 | stream.on('error', function (err) { |
| 956 | assert.match(err.message, /Missing dictionary/); |
| 957 | resolve(); |
| 958 | }); |
| 959 | |
| 960 | stream.write(input); |
| 961 | await promise; |
| 962 | } |
| 963 | |
| 964 | { |
| 965 | const { promise, resolve } = Promise.withResolvers(); |
| 966 | const stream = zlib.createInflate({ dictionary: Buffer.from('fail') }); |
| 967 | |
| 968 | stream.on('error', function (err) { |
| 969 | assert.match(err.message, /Bad dictionary/); |
| 970 | resolve(); |
| 971 | }); |
| 972 | |
| 973 | stream.write(input); |
| 974 | await promise; |
| 975 | } |
| 976 | |
| 977 | { |
| 978 | const { promise, resolve } = Promise.withResolvers(); |
| 979 | const stream = zlib.createInflateRaw({ dictionary: Buffer.from('fail') }); |
| 980 | |
| 981 | stream.on('error', function (err) { |
| 982 | // It's not possible to separate invalid dict and invalid data when using |
| 983 | // the raw format |
| 984 | assert.match( |
| 985 | err.message, |
| 986 | /(invalid|Operation-Ending-Supplemental Code is 0x12)/ |
| 987 | ); |
| 988 | resolve(); |
| 989 | }); |
| 990 | |
| 991 | stream.write(input); |
| 992 | await promise; |
| 993 | } |
| 994 | }, |
| 995 | }; |
| 996 | |
| 997 | // Tests are taken from: |
| 998 | // https://github.com/nodejs/node/blob/431ac161e65698152de197703fb30c89da2b6686/test/parallel/test-zlib-close-in-ondata.js |
| 999 | export const zlibCloseInOnData = { |
| 1000 | async test() { |
| 1001 | const { promise, resolve } = Promise.withResolvers(); |
| 1002 | const ts = zlib.createGzip(); |
| 1003 | const buf = Buffer.alloc(1024 * 1024 * 20); |
| 1004 | |
| 1005 | ts.on('data', function () { |
| 1006 | ts.close(); |
| 1007 | resolve(); |
| 1008 | }); |
| 1009 | ts.end(buf); |
| 1010 | await promise; |
| 1011 | }, |
| 1012 | }; |
| 1013 | |
| 1014 | // Tests are taken from: |
| 1015 | // https://github.com/nodejs/node/blob/431ac161e65698152de197703fb30c89da2b6686/test/parallel/test-zlib-flush-flags.js |
| 1016 | export const zlibFlushFlags = { |
| 1017 | test() { |
| 1018 | zlib.createGzip({ flush: zlib.constants.Z_SYNC_FLUSH }); |
| 1019 | |
| 1020 | assert.throws(() => zlib.createGzip({ flush: 'foobar' }), { |
| 1021 | code: 'ERR_INVALID_ARG_TYPE', |
| 1022 | name: 'TypeError', |
| 1023 | message: |
| 1024 | 'The "options.flush" property must be of type number. ' + |
| 1025 | "Received type string ('foobar')", |
| 1026 | }); |
| 1027 | |
| 1028 | assert.throws(() => zlib.createGzip({ flush: 10000 }), { |
| 1029 | code: 'ERR_OUT_OF_RANGE', |
| 1030 | name: 'RangeError', |
| 1031 | message: |
| 1032 | 'The value of "options.flush" is out of range. It must ' + |
| 1033 | 'be >= 0 and <= 5. Received 10000', |
| 1034 | }); |
| 1035 | |
| 1036 | zlib.createGzip({ finishFlush: zlib.constants.Z_SYNC_FLUSH }); |
| 1037 | |
| 1038 | assert.throws(() => zlib.createGzip({ finishFlush: 'foobar' }), { |
| 1039 | code: 'ERR_INVALID_ARG_TYPE', |
| 1040 | name: 'TypeError', |
| 1041 | message: |
| 1042 | 'The "options.finishFlush" property must be of type number. ' + |
| 1043 | "Received type string ('foobar')", |
| 1044 | }); |
| 1045 | |
| 1046 | assert.throws(() => zlib.createGzip({ finishFlush: 10000 }), { |
| 1047 | code: 'ERR_OUT_OF_RANGE', |
| 1048 | name: 'RangeError', |
| 1049 | message: |
| 1050 | 'The value of "options.finishFlush" is out of range. It must ' + |
| 1051 | 'be >= 0 and <= 5. Received 10000', |
| 1052 | }); |
| 1053 | }, |
| 1054 | }; |
| 1055 | |
| 1056 | // Tests are taken from: |
| 1057 | // https://github.com/nodejs/node/blob/431ac161e65698152de197703fb30c89da2b6686/test/parallel/test-zlib-reset-before-write.js |
| 1058 | export const zlibResetBeforeWrite = { |
| 1059 | async test() { |
| 1060 | // Tests that zlib streams support .reset() and .params() |
| 1061 | // before the first write. That is important to ensure that |
| 1062 | // lazy init of zlib native library handles these cases. |
| 1063 | |
| 1064 | for (const fn of [ |
| 1065 | (z, cb) => { |
| 1066 | z.reset(); |
| 1067 | cb(); |
| 1068 | }, |
| 1069 | (z, cb) => z.params(0, zlib.constants.Z_DEFAULT_STRATEGY, cb), |
| 1070 | ]) { |
| 1071 | const { promise, resolve, reject } = Promise.withResolvers(); |
| 1072 | const deflate = zlib.createDeflate(); |
| 1073 | const inflate = zlib.createInflate(); |
| 1074 | |
| 1075 | deflate.pipe(inflate); |
| 1076 | |
| 1077 | const output = []; |
| 1078 | inflate |
| 1079 | .on('error', reject) |
| 1080 | .on('data', (chunk) => output.push(chunk)) |
| 1081 | .on('end', resolve); |
| 1082 | |
| 1083 | fn(deflate, () => { |
| 1084 | fn(inflate, () => { |
| 1085 | deflate.write('abc'); |
| 1086 | deflate.end(); |
| 1087 | }); |
| 1088 | }); |
| 1089 | |
| 1090 | await promise; |
| 1091 | |
| 1092 | assert.strictEqual(Buffer.concat(output).toString(), 'abc'); |
| 1093 | } |
| 1094 | }, |
| 1095 | }; |
| 1096 | |
| 1097 | // Tests are taken from: |
| 1098 | // https://github.com/nodejs/node/blob/6bf7b6e342f97cf48319e0bc251200fabe132c21/test/parallel/test-zlib-invalid-input.js |
| 1099 | export const zlibInvalidInput = { |
| 1100 | async test() { |
| 1101 | const nonStringInputs = [1, true, { a: 1 }, ['a']]; |
| 1102 | |
| 1103 | // zlib.Unzip classes need to get valid data, or else they'll throw. |
| 1104 | const unzips = [ |
| 1105 | new zlib.Unzip(), |
| 1106 | new zlib.Gunzip(), |
| 1107 | new zlib.Inflate(), |
| 1108 | new zlib.InflateRaw(), |
| 1109 | new zlib.BrotliDecompress(), |
| 1110 | ]; |
| 1111 | |
| 1112 | for (const input of nonStringInputs) { |
| 1113 | assert.throws( |
| 1114 | () => { |
| 1115 | zlib.gunzip(input); |
| 1116 | }, |
| 1117 | { |
| 1118 | name: 'TypeError', |
| 1119 | } |
| 1120 | ); |
| 1121 | } |
| 1122 | |
| 1123 | for (const uz of unzips) { |
| 1124 | const { promise, resolve, reject } = Promise.withResolvers(); |
| 1125 | uz.on('error', resolve); |
| 1126 | uz.on('end', reject); |
| 1127 | |
| 1128 | // This will trigger error event |
| 1129 | uz.write('this is not valid compressed data.'); |
| 1130 | await promise; |
| 1131 | } |
| 1132 | }, |
| 1133 | }; |
| 1134 | |
| 1135 | // Tests are taken from: |
| 1136 | // https://github.com/nodejs/node/blob/5e6aab0ecad6394e538e06357d6e16e155951a8b/test/parallel/test-zlib-unzip-one-byte-chunks.js |
| 1137 | export const zlibUnzipOneByteChunks = { |
| 1138 | async test() { |
| 1139 | const { promise, resolve, reject } = Promise.withResolvers(); |
| 1140 | const data = Buffer.concat([zlib.gzipSync('abc'), zlib.gzipSync('def')]); |
| 1141 | |
| 1142 | const resultBuffers = []; |
| 1143 | |
| 1144 | const unzip = zlib |
| 1145 | .createUnzip() |
| 1146 | .on('error', reject) |
| 1147 | .on('data', (data) => resultBuffers.push(data)) |
| 1148 | .on('finish', resolve); |
| 1149 | |
| 1150 | for (let i = 0; i < data.length; i++) { |
| 1151 | // Write each single byte individually. |
| 1152 | unzip.write(Buffer.from([data[i]])); |
| 1153 | } |
| 1154 | |
| 1155 | unzip.end(); |
| 1156 | await promise; |
| 1157 | assert.strictEqual(Buffer.concat(resultBuffers).toString(), 'abcdef'); |
| 1158 | }, |
| 1159 | }; |
| 1160 | |
| 1161 | // Tests are taken from: |
| 1162 | // https://github.com/nodejs/node/blob/4f14eb15454b9f6ae7f0145947debd2c79a2a84f/test/parallel/test-zlib-truncated.js |
| 1163 | export const zlibTruncated = { |
| 1164 | async test() { |
| 1165 | const inputString = |
| 1166 | 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli' + |
| 1167 | 't. Morbi faucibus, purus at gravida dictum, libero arcu ' + |
| 1168 | 'convallis lacus, in commodo libero metus eu nisi. Nullam' + |
| 1169 | ' commodo, neque nec porta placerat, nisi est fermentum a' + |
| 1170 | 'ugue, vitae gravida tellus sapien sit amet tellus. Aenea' + |
| 1171 | 'n non diam orci. Proin quis elit turpis. Suspendisse non' + |
| 1172 | ' diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu' + |
| 1173 | 'm arcu mi, sodales non suscipit id, ultrices ut massa. S' + |
| 1174 | 'ed ac sem sit amet arcu malesuada fermentum. Nunc sed. '; |
| 1175 | |
| 1176 | const errMessage = /unexpected end of file/; |
| 1177 | |
| 1178 | const cases = [ |
| 1179 | { comp: 'gzip', decomp: 'gunzip', decompSync: 'gunzipSync' }, |
| 1180 | { comp: 'gzip', decomp: 'unzip', decompSync: 'unzipSync' }, |
| 1181 | { comp: 'deflate', decomp: 'inflate', decompSync: 'inflateSync' }, |
| 1182 | { |
| 1183 | comp: 'deflateRaw', |
| 1184 | decomp: 'inflateRaw', |
| 1185 | decompSync: 'inflateRawSync', |
| 1186 | }, |
| 1187 | ]; |
| 1188 | for (const methods of cases) { |
| 1189 | const { promise, resolve } = Promise.withResolvers(); |
| 1190 | zlib[methods.comp](inputString, async function (err, compressed) { |
| 1191 | assert.ifError(err); |
| 1192 | const truncated = compressed.slice(0, compressed.length / 2); |
| 1193 | const toUTF8 = (buffer) => buffer.toString('utf-8'); |
| 1194 | |
| 1195 | // sync sanity |
| 1196 | const decompressed = zlib[methods.decompSync](compressed); |
| 1197 | assert.strictEqual(toUTF8(decompressed), inputString); |
| 1198 | |
| 1199 | // async sanity |
| 1200 | { |
| 1201 | const { promise, resolve } = Promise.withResolvers(); |
| 1202 | zlib[methods.decomp](compressed, function (err, result) { |
| 1203 | assert.ifError(err); |
| 1204 | assert.strictEqual(toUTF8(result), inputString); |
| 1205 | resolve(); |
| 1206 | }); |
| 1207 | |
| 1208 | await promise; |
| 1209 | } |
| 1210 | |
| 1211 | // Sync truncated input test |
| 1212 | assert.throws(function () { |
| 1213 | zlib[methods.decompSync](truncated); |
| 1214 | }, errMessage); |
| 1215 | |
| 1216 | // Async truncated input test |
| 1217 | { |
| 1218 | const { promise, resolve } = Promise.withResolvers(); |
| 1219 | zlib[methods.decomp](truncated, function (err) { |
| 1220 | assert.match(err.message, errMessage); |
| 1221 | resolve(); |
| 1222 | }); |
| 1223 | |
| 1224 | await promise; |
| 1225 | } |
| 1226 | |
| 1227 | const syncFlushOpt = { finishFlush: zlib.constants.Z_SYNC_FLUSH }; |
| 1228 | |
| 1229 | // Sync truncated input test, finishFlush = Z_SYNC_FLUSH |
| 1230 | const result = toUTF8( |
| 1231 | zlib[methods.decompSync](truncated, syncFlushOpt) |
| 1232 | ); |
| 1233 | assert.strictEqual(result, inputString.slice(0, result.length)); |
| 1234 | |
| 1235 | // Async truncated input test, finishFlush = Z_SYNC_FLUSH |
| 1236 | { |
| 1237 | const { promise, resolve } = Promise.withResolvers(); |
| 1238 | zlib[methods.decomp]( |
| 1239 | truncated, |
| 1240 | syncFlushOpt, |
| 1241 | function (err, decompressed) { |
| 1242 | assert.ifError(err); |
| 1243 | const result = toUTF8(decompressed); |
| 1244 | assert.strictEqual(result, inputString.slice(0, result.length)); |
| 1245 | resolve(); |
| 1246 | } |
| 1247 | ); |
| 1248 | await promise; |
| 1249 | } |
| 1250 | |
| 1251 | resolve(); |
| 1252 | }); |
| 1253 | |
| 1254 | await promise; |
| 1255 | } |
| 1256 | }, |
| 1257 | }; |
| 1258 | |
| 1259 | // Tests are taken from: |
| 1260 | // https://github.com/nodejs/node/blob/beabcec41ca456e7e895e276acbc5f2d93db032f/test/parallel/test-zlib-from-concatenated-gzip.js |
| 1261 | export const zlibFromConcatenatedGzip = { |
| 1262 | async test() { |
| 1263 | const abc = 'abc'; |
| 1264 | const def = 'def'; |
| 1265 | |
| 1266 | const abcEncoded = zlib.gzipSync(abc); |
| 1267 | const defEncoded = zlib.gzipSync(def); |
| 1268 | |
| 1269 | const data = Buffer.concat([abcEncoded, defEncoded]); |
| 1270 | |
| 1271 | assert.strictEqual(zlib.gunzipSync(data).toString(), abc + def); |
| 1272 | |
| 1273 | { |
| 1274 | const { promise, resolve } = Promise.withResolvers(); |
| 1275 | zlib.gunzip(data, (err, result) => { |
| 1276 | assert.ifError(err); |
| 1277 | assert.strictEqual(result.toString(), abc + def); |
| 1278 | resolve(); |
| 1279 | }); |
| 1280 | |
| 1281 | await promise; |
| 1282 | } |
| 1283 | |
| 1284 | { |
| 1285 | const { promise, resolve } = Promise.withResolvers(); |
| 1286 | zlib.unzip(data, (err, result) => { |
| 1287 | assert.ifError(err); |
| 1288 | assert.strictEqual(result.toString(), abc + def); |
| 1289 | resolve(); |
| 1290 | }); |
| 1291 | |
| 1292 | await promise; |
| 1293 | } |
| 1294 | |
| 1295 | // Multi-member support does not apply to zlib inflate/deflate. |
| 1296 | { |
| 1297 | const { promise, resolve } = Promise.withResolvers(); |
| 1298 | zlib.unzip( |
| 1299 | Buffer.concat([zlib.deflateSync('abc'), zlib.deflateSync('def')]), |
| 1300 | (err, result) => { |
| 1301 | assert.ifError(err); |
| 1302 | assert.strictEqual(result.toString(), abc); |
| 1303 | resolve(); |
| 1304 | } |
| 1305 | ); |
| 1306 | |
| 1307 | await promise; |
| 1308 | } |
| 1309 | |
| 1310 | { |
| 1311 | // Files that have the "right" magic bytes for starting a new gzip member |
| 1312 | // in the middle of themselves, even if they are part of a single |
| 1313 | // regularly compressed member |
| 1314 | const pmmDataZlib = Buffer.from( |
| 1315 | 'eJztwTEBAAAAwqD1T+1vBqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + |
| 1316 | 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + |
| 1317 | 'AAAAAAAAAAAAAAAAAAAAAAAAAAAIAz+w4AAQ==', |
| 1318 | 'base64' |
| 1319 | ); |
| 1320 | const pmmDataGz = Buffer.from( |
| 1321 | 'H4sIAMyK8lYCA+3BMQEAAADCoPVP7WENoAAAAAAAAAAAAAAAAAAAAAAAH4sAAAAAAAAAAAAAAAAA' + |
| 1322 | 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + |
| 1323 | 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABuGn4Inv/6AQA=', |
| 1324 | 'base64' |
| 1325 | ); |
| 1326 | const pmmExpected = zlib.inflateSync(pmmDataZlib); |
| 1327 | const pmmResultBuffers = []; |
| 1328 | const { promise, resolve, reject } = Promise.withResolvers(); |
| 1329 | |
| 1330 | Readable.from([pmmDataGz]) |
| 1331 | .pipe(zlib.createGunzip()) |
| 1332 | .on('error', reject) |
| 1333 | .on('data', (data) => pmmResultBuffers.push(data)) |
| 1334 | .on('finish', resolve); |
| 1335 | |
| 1336 | await promise; |
| 1337 | // Result should match original random garbage |
| 1338 | assert.deepStrictEqual(Buffer.concat(pmmResultBuffers), pmmExpected); |
| 1339 | } |
| 1340 | |
| 1341 | // Test that the next gzip member can wrap around the input buffer boundary |
| 1342 | for (const offset of [0, 1, 2, 3, 4, defEncoded.length]) { |
| 1343 | const { promise, resolve, reject } = Promise.withResolvers(); |
| 1344 | const resultBuffers = []; |
| 1345 | |
| 1346 | const unzip = zlib |
| 1347 | .createGunzip() |
| 1348 | .on('error', reject) |
| 1349 | .on('data', (data) => resultBuffers.push(data)) |
| 1350 | .on('finish', resolve); |
| 1351 | |
| 1352 | // First write: write "abc" + the first bytes of "def" |
| 1353 | unzip.write(Buffer.concat([abcEncoded, defEncoded.slice(0, offset)])); |
| 1354 | |
| 1355 | // Write remaining bytes of "def" |
| 1356 | unzip.end(defEncoded.slice(offset)); |
| 1357 | |
| 1358 | await promise; |
| 1359 | |
| 1360 | assert.strictEqual( |
| 1361 | Buffer.concat(resultBuffers).toString(), |
| 1362 | 'abcdef', |
| 1363 | `result should match original input (offset = ${offset})` |
| 1364 | ); |
| 1365 | } |
| 1366 | }, |
| 1367 | }; |
| 1368 | |
| 1369 | // Test taken from: |
| 1370 | // https://github.com/nodejs/node/blob/fc02b88f89f8d5abf5ee4414a1026444c18d77b3/test/parallel/test-zlib-not-string-or-buffer.js |
| 1371 | export const zlibNotStringOrBuffer = { |
| 1372 | test() { |
| 1373 | for (const input of [ |
| 1374 | undefined, |
| 1375 | null, |
| 1376 | true, |
| 1377 | false, |
| 1378 | 0, |
| 1379 | 1, |
| 1380 | [1, 2, 3], |
| 1381 | { foo: 'bar' }, |
| 1382 | ]) { |
| 1383 | assert.throws( |
| 1384 | () => zlib.deflateSync(input), |
| 1385 | // TODO(soon): Use same error code as NodeJS |
| 1386 | { |
| 1387 | name: 'TypeError', |
| 1388 | } |
| 1389 | ); |
| 1390 | } |
| 1391 | }, |
| 1392 | }; |
| 1393 | |
| 1394 | // Test taken from: |
| 1395 | // https://github.com/nodejs/node/blob/fc02b88f89f8d5abf5ee4414a1026444c18d77b3/test/parallel/test-zlib-from-gzip-with-trailing-garbage.js |
| 1396 | export const zlibFromGzipWithTrailingGarbage = { |
| 1397 | async test() { |
| 1398 | // Should ignore trailing null-bytes |
| 1399 | let data = Buffer.concat([ |
| 1400 | zlib.gzipSync('abc'), |
| 1401 | zlib.gzipSync('def'), |
| 1402 | Buffer.alloc(10), |
| 1403 | ]); |
| 1404 | |
| 1405 | assert.strictEqual(zlib.gunzipSync(data).toString(), 'abcdef'); |
| 1406 | |
| 1407 | { |
| 1408 | const { promise, resolve } = Promise.withResolvers(); |
| 1409 | |
| 1410 | zlib.gunzip(data, (err, result) => { |
| 1411 | assert.ifError(err); |
| 1412 | assert.strictEqual( |
| 1413 | result.toString(), |
| 1414 | 'abcdef', |
| 1415 | `result '${result.toString()}' should match original string` |
| 1416 | ); |
| 1417 | resolve(); |
| 1418 | }); |
| 1419 | |
| 1420 | await promise; |
| 1421 | } |
| 1422 | |
| 1423 | // If the trailing garbage happens to look like a gzip header, it should |
| 1424 | // throw an error. |
| 1425 | data = Buffer.concat([ |
| 1426 | zlib.gzipSync('abc'), |
| 1427 | zlib.gzipSync('def'), |
| 1428 | Buffer.from([0x1f, 0x8b, 0xff, 0xff]), |
| 1429 | Buffer.alloc(10), |
| 1430 | ]); |
| 1431 | |
| 1432 | assert.throws( |
| 1433 | () => zlib.gunzipSync(data), |
| 1434 | /^Error: unknown compression method$/ |
| 1435 | ); |
| 1436 | |
| 1437 | { |
| 1438 | const { promise, resolve } = Promise.withResolvers(); |
| 1439 | zlib.gunzip(data, (err, result) => { |
| 1440 | // TODO(soon): use same error code as NodeJS |
| 1441 | // TODO(soon): Do our messages have a redundant "Error: " word? |
| 1442 | assert.strictEqual(err.name, 'Error'); |
| 1443 | assert.match(err.message, /unknown compression method/); |
| 1444 | assert.strictEqual(result, undefined); |
| 1445 | resolve(); |
| 1446 | }); |
| 1447 | |
| 1448 | await promise; |
| 1449 | } |
| 1450 | |
| 1451 | // In this case the trailing junk is too short to be a gzip segment |
| 1452 | // So we ignore it and decompression succeeds. |
| 1453 | data = Buffer.concat([ |
| 1454 | zlib.gzipSync('abc'), |
| 1455 | zlib.gzipSync('def'), |
| 1456 | Buffer.from([0x1f, 0x8b, 0xff, 0xff]), |
| 1457 | ]); |
| 1458 | |
| 1459 | assert.throws( |
| 1460 | () => zlib.gunzipSync(data), |
| 1461 | /^Error: unknown compression method$/ |
| 1462 | ); |
| 1463 | |
| 1464 | { |
| 1465 | const { promise, resolve } = Promise.withResolvers(); |
| 1466 | zlib.gunzip(data, (err, result) => { |
| 1467 | // TODO(soon): Use same error code as NodeJS |
| 1468 | // TODO(soon): Do our messages have a redundant "Error: " word? |
| 1469 | assert.strictEqual(err.name, 'Error'); |
| 1470 | assert.match(err.message, /unknown compression method/); |
| 1471 | assert.strictEqual(result, undefined); |
| 1472 | resolve(); |
| 1473 | }); |
| 1474 | |
| 1475 | await promise; |
| 1476 | } |
| 1477 | }, |
| 1478 | }; |
| 1479 | |
| 1480 | // Test taken from |
| 1481 | // https://github.com/nodejs/node/blob/fc02b88f89f8d5abf5ee4414a1026444c18d77b3/test/parallel/test-zlib-from-string.js |
| 1482 | export const zlibFromString = { |
| 1483 | async test() { |
| 1484 | const inputString = |
| 1485 | 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli' + |
| 1486 | 't. Morbi faucibus, purus at gravida dictum, libero arcu ' + |
| 1487 | 'convallis lacus, in commodo libero metus eu nisi. Nullam' + |
| 1488 | ' commodo, neque nec porta placerat, nisi est fermentum a' + |
| 1489 | 'ugue, vitae gravida tellus sapien sit amet tellus. Aenea' + |
| 1490 | 'n non diam orci. Proin quis elit turpis. Suspendisse non' + |
| 1491 | ' diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu' + |
| 1492 | 'm arcu mi, sodales non suscipit id, ultrices ut massa. S' + |
| 1493 | 'ed ac sem sit amet arcu malesuada fermentum. Nunc sed. '; |
| 1494 | const expectedBase64Deflate = |
| 1495 | 'eJxdUUtOQzEMvMoc4OndgT0gJCT2buJWlpI4jePeqZfpmX' + |
| 1496 | 'AKLRKbLOzx/HK73q6vOrhCunlF1qIDJhNUeW5I2ozT5OkD' + |
| 1497 | 'lKWLJWkncJG5403HQXAkT3Jw29B9uIEmToMukglZ0vS6oc' + |
| 1498 | 'iBh4JG8sV4oVLEUCitK2kxq1WzPnChHDzsaGKy491LofoA' + |
| 1499 | 'bWh8do43oeuYhB5EPCjcLjzYJo48KrfQBvnJecNFJvHT1+' + |
| 1500 | 'RSQsGoC7dn2t/xjhduTA1NWyQIZR0pbHwMDatnD+crPqKS' + |
| 1501 | 'qGPHp1vnlsWM/07ubf7bheF7kqSj84Bm0R1fYTfaK8vqqq' + |
| 1502 | 'fKBtNMhe3OZh6N95CTvMX5HJJi4xOVzCgUOIMSLH7wmeOH' + |
| 1503 | 'aFE4RdpnGavKtrB5xzfO/Ll9'; |
| 1504 | const expectedBase64Gzip = |
| 1505 | 'H4sIAAAAAAAAA11RS05DMQy8yhzg6d2BPSAkJPZu4laWkjiN4' + |
| 1506 | '96pl+mZcAotEpss7PH8crverq86uEK6eUXWogMmE1R5bkjajN' + |
| 1507 | 'Pk6QOUpYslaSdwkbnjTcdBcCRPcnDb0H24gSZOgy6SCVnS9Lq' + |
| 1508 | 'hyIGHgkbyxXihUsRQKK0raTGrVbM+cKEcPOxoYrLj3Uuh+gBt' + |
| 1509 | 'aHx2jjeh65iEHkQ8KNwuPNgmjjwqt9AG+cl5w0Um8dPX5FJCw' + |
| 1510 | 'agLt2fa3/GOF25MDU1bJAhlHSlsfAwNq2cP5ys+opKoY8enW+' + |
| 1511 | 'eWxYz/Tu5t/tuF4XuSpKPzgGbRHV9hN9ory+qqp8oG00yF7c5' + |
| 1512 | 'mHo33kJO8xfkckmLjE5XMKBQ4gxIsfvCZ44doUThF2mcZq8q2' + |
| 1513 | 'sHnHNzRtagj5AQAA'; |
| 1514 | |
| 1515 | { |
| 1516 | const { promise, resolve } = Promise.withResolvers(); |
| 1517 | zlib.deflate(inputString, (err, buffer) => { |
| 1518 | zlib.inflate(buffer, (err, inflated) => { |
| 1519 | assert.strictEqual(inflated.toString(), inputString); |
| 1520 | resolve(); |
| 1521 | }); |
| 1522 | }); |
| 1523 | await promise; |
| 1524 | } |
| 1525 | |
| 1526 | { |
| 1527 | const { promise, resolve } = Promise.withResolvers(); |
| 1528 | zlib.gzip(inputString, (err, buffer) => { |
| 1529 | assert.ifError(err); |
| 1530 | // Can't actually guarantee that we'll get exactly the same |
| 1531 | // deflated bytes when we compress a string, since the header |
| 1532 | // depends on stuff other than the input string itself. |
| 1533 | // However, decrypting it should definitely yield the same |
| 1534 | // result that we're expecting, and this should match what we get |
| 1535 | // from inflating the known valid deflate data. |
| 1536 | zlib.gunzip(buffer, (err, gunzipped) => { |
| 1537 | assert.ifError(err); |
| 1538 | assert.strictEqual(gunzipped.toString(), inputString); |
| 1539 | resolve(); |
| 1540 | }); |
| 1541 | }); |
| 1542 | await promise; |
| 1543 | } |
| 1544 | |
| 1545 | let buffer = Buffer.from(expectedBase64Deflate, 'base64'); |
| 1546 | { |
| 1547 | const { promise, resolve } = Promise.withResolvers(); |
| 1548 | zlib.unzip(buffer, (err, buffer) => { |
| 1549 | assert.ifError(err); |
| 1550 | assert.strictEqual(buffer.toString(), inputString); |
| 1551 | resolve(); |
| 1552 | }); |
| 1553 | |
| 1554 | await promise; |
| 1555 | } |
| 1556 | |
| 1557 | buffer = Buffer.from(expectedBase64Gzip, 'base64'); |
| 1558 | |
| 1559 | { |
| 1560 | const { promise, resolve } = Promise.withResolvers(); |
| 1561 | zlib.unzip(buffer, (err, buffer) => { |
| 1562 | assert.ifError(err); |
| 1563 | assert.strictEqual(buffer.toString(), inputString); |
| 1564 | resolve(); |
| 1565 | }); |
| 1566 | await promise; |
| 1567 | } |
| 1568 | }, |
| 1569 | }; |
| 1570 | |
| 1571 | // Test taken from |
| 1572 | // https://github.com/nodejs/node/blob/fc02b88f89f8d5abf5ee4414a1026444c18d77b3/test/parallel/test-zlib-empty-buffer.js |
| 1573 | export const zlibEmptyBuffer = { |
| 1574 | async test() { |
| 1575 | const emptyBuffer = Buffer.alloc(0); |
| 1576 | |
| 1577 | for (const [compress, decompress, method] of [ |
| 1578 | [zlib.deflateRawSync, zlib.inflateRawSync, 'raw sync'], |
| 1579 | [zlib.deflateSync, zlib.inflateSync, 'deflate sync'], |
| 1580 | [zlib.gzipSync, zlib.gunzipSync, 'gzip sync'], |
| 1581 | [zlib.brotliCompressSync, zlib.brotliDecompressSync, 'br sync'], |
| 1582 | [promisify(zlib.deflateRaw), promisify(zlib.inflateRaw), 'raw'], |
| 1583 | [promisify(zlib.deflate), promisify(zlib.inflate), 'deflate'], |
| 1584 | [promisify(zlib.gzip), promisify(zlib.gunzip), 'gzip'], |
| 1585 | [promisify(zlib.brotliCompress), promisify(zlib.brotliDecompress), 'br'], |
| 1586 | ]) { |
| 1587 | const compressed = await compress(emptyBuffer); |
| 1588 | const decompressed = await decompress(compressed); |
| 1589 | assert.deepStrictEqual( |
| 1590 | emptyBuffer, |
| 1591 | decompressed, |
| 1592 | `Expected ${inspect(compressed)} to match ${inspect(decompressed)} ` + |
| 1593 | `to match for ${method}` |
| 1594 | ); |
| 1595 | } |
| 1596 | }, |
| 1597 | }; |
| 1598 | |
| 1599 | // Test taken from: |
| 1600 | // https://github.com/nodejs/node/blob/d75e253c506310ea6728329981beb3284fa431b5/test/parallel/test-zlib-flush.js |
| 1601 | export const zlibFlush = { |
| 1602 | async test() { |
| 1603 | const opts = { level: 0 }; |
| 1604 | const deflater = zlib.createDeflate(opts); |
| 1605 | |
| 1606 | const chunk = Buffer.from('/9j/4AAQSkZJRgABAQEASA==', 'base64'); |
| 1607 | const expectedNone = Buffer.from([0x78, 0x01]); |
| 1608 | const blkhdr = Buffer.from([0x00, 0x10, 0x00, 0xef, 0xff]); |
| 1609 | const adler32 = Buffer.from([0x00, 0x00, 0x00, 0xff, 0xff]); |
| 1610 | const expectedFull = Buffer.concat([blkhdr, chunk, adler32]); |
| 1611 | let actualNone; |
| 1612 | let actualFull; |
| 1613 | |
| 1614 | const { promise, resolve } = Promise.withResolvers(); |
| 1615 | deflater.write(chunk, function () { |
| 1616 | deflater.flush(zlib.constants.Z_NO_FLUSH, function () { |
| 1617 | actualNone = deflater.read(); |
| 1618 | deflater.flush(function () { |
| 1619 | const bufs = []; |
| 1620 | let buf; |
| 1621 | while ((buf = deflater.read()) !== null) bufs.push(buf); |
| 1622 | actualFull = Buffer.concat(bufs); |
| 1623 | |
| 1624 | resolve(); |
| 1625 | }); |
| 1626 | }); |
| 1627 | }); |
| 1628 | |
| 1629 | await promise; |
| 1630 | assert.deepStrictEqual(actualNone, expectedNone); |
| 1631 | assert.deepStrictEqual(actualFull, expectedFull); |
| 1632 | }, |
| 1633 | }; |
| 1634 | |
| 1635 | // Test taken from: |
| 1636 | // https://github.com/nodejs/node/blob/9bdf2ee1d184e7ec5c690319e068894ed324b595/test/parallel/test-zlib-dictionary.js |
| 1637 | export const zlibDictionary = { |
| 1638 | async test() { |
| 1639 | const spdyDict = Buffer.from( |
| 1640 | [ |
| 1641 | 'optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-', |
| 1642 | 'languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi', |
| 1643 | 'f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser', |
| 1644 | '-agent10010120020120220320420520630030130230330430530630740040140240340440', |
| 1645 | '5406407408409410411412413414415416417500501502503504505accept-rangesageeta', |
| 1646 | 'glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic', |
| 1647 | 'ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran', |
| 1648 | 'sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati', |
| 1649 | 'oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo', |
| 1650 | 'ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe', |
| 1651 | 'pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic', |
| 1652 | 'ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1', |
| 1653 | '.1statusversionurl\0', |
| 1654 | ].join('') |
| 1655 | ); |
| 1656 | |
| 1657 | const input = [ |
| 1658 | 'HTTP/1.1 200 Ok', |
| 1659 | 'Server: node.js', |
| 1660 | 'Content-Length: 0', |
| 1661 | '', |
| 1662 | ].join('\r\n'); |
| 1663 | |
| 1664 | // basicDictionaryTest |
| 1665 | { |
| 1666 | let { promise, resolve, reject } = Promise.withResolvers(); |
| 1667 | let output = ''; |
| 1668 | const deflate = zlib.createDeflate({ dictionary: spdyDict }); |
| 1669 | const inflate = zlib.createInflate({ dictionary: spdyDict }); |
| 1670 | inflate.setEncoding('utf-8'); |
| 1671 | |
| 1672 | deflate |
| 1673 | .on('data', function (chunk) { |
| 1674 | inflate.write(chunk); |
| 1675 | }) |
| 1676 | .on('error', reject) |
| 1677 | .on('end', function () { |
| 1678 | inflate.end(); |
| 1679 | }); |
| 1680 | |
| 1681 | inflate |
| 1682 | .on('error', reject) |
| 1683 | .on('end', resolve) |
| 1684 | .on('data', function (chunk) { |
| 1685 | output += chunk; |
| 1686 | }); |
| 1687 | |
| 1688 | deflate.write(input); |
| 1689 | deflate.end(); |
| 1690 | await promise; |
| 1691 | assert.strictEqual(input, output); |
| 1692 | } |
| 1693 | |
| 1694 | // deflateResetDictionaryTest |
| 1695 | { |
| 1696 | let { promise, resolve, reject } = Promise.withResolvers(); |
| 1697 | let doneReset = false; |
| 1698 | let output = ''; |
| 1699 | const deflate = zlib.createDeflate({ dictionary: spdyDict }); |
| 1700 | const inflate = zlib.createInflate({ dictionary: spdyDict }); |
| 1701 | inflate.setEncoding('utf-8'); |
| 1702 | |
| 1703 | deflate |
| 1704 | .on('data', function (chunk) { |
| 1705 | if (doneReset) inflate.write(chunk); |
| 1706 | }) |
| 1707 | .on('error', reject) |
| 1708 | .on('end', function () { |
| 1709 | inflate.end(); |
| 1710 | }); |
| 1711 | |
| 1712 | inflate |
| 1713 | .on('data', function (chunk) { |
| 1714 | output += chunk; |
| 1715 | }) |
| 1716 | .on('error', reject) |
| 1717 | .on('end', resolve); |
| 1718 | |
| 1719 | deflate.write(input); |
| 1720 | deflate.flush(function () { |
| 1721 | deflate.reset(); |
| 1722 | doneReset = true; |
| 1723 | deflate.write(input); |
| 1724 | deflate.end(); |
| 1725 | }); |
| 1726 | |
| 1727 | await promise; |
| 1728 | assert.strictEqual(input, output); |
| 1729 | } |
| 1730 | |
| 1731 | // rawDictionaryTest |
| 1732 | { |
| 1733 | let { promise, resolve, reject } = Promise.withResolvers(); |
| 1734 | let output = ''; |
| 1735 | const deflate = zlib.createDeflateRaw({ dictionary: spdyDict }); |
| 1736 | const inflate = zlib.createInflateRaw({ dictionary: spdyDict }); |
| 1737 | inflate.setEncoding('utf-8'); |
| 1738 | |
| 1739 | deflate |
| 1740 | .on('data', function (chunk) { |
| 1741 | inflate.write(chunk); |
| 1742 | }) |
| 1743 | .on('error', reject) |
| 1744 | .on('end', function () { |
| 1745 | inflate.end(); |
| 1746 | }); |
| 1747 | inflate |
| 1748 | .on('data', function (chunk) { |
| 1749 | output += chunk; |
| 1750 | }) |
| 1751 | .on('error', reject) |
| 1752 | .on('end', resolve); |
| 1753 | |
| 1754 | deflate.write(input); |
| 1755 | deflate.end(); |
| 1756 | |
| 1757 | await promise; |
| 1758 | assert.strictEqual(input, output); |
| 1759 | } |
| 1760 | |
| 1761 | // deflateRawResetDictionaryTest |
| 1762 | { |
| 1763 | let { promise, resolve, reject } = Promise.withResolvers(); |
| 1764 | let doneReset = false; |
| 1765 | let output = ''; |
| 1766 | const deflate = zlib.createDeflateRaw({ dictionary: spdyDict }); |
| 1767 | const inflate = zlib.createInflateRaw({ dictionary: spdyDict }); |
| 1768 | inflate.setEncoding('utf-8'); |
| 1769 | |
| 1770 | deflate |
| 1771 | .on('data', function (chunk) { |
| 1772 | if (doneReset) inflate.write(chunk); |
| 1773 | }) |
| 1774 | .on('error', reject) |
| 1775 | .on('end', function () { |
| 1776 | inflate.end(); |
| 1777 | }); |
| 1778 | |
| 1779 | inflate |
| 1780 | .on('data', function (chunk) { |
| 1781 | output += chunk; |
| 1782 | }) |
| 1783 | .on('error', reject) |
| 1784 | .on('end', resolve); |
| 1785 | |
| 1786 | deflate.write(input); |
| 1787 | deflate.flush(function () { |
| 1788 | deflate.reset(); |
| 1789 | doneReset = true; |
| 1790 | deflate.write(input); |
| 1791 | deflate.end(); |
| 1792 | }); |
| 1793 | |
| 1794 | await promise; |
| 1795 | assert.strictEqual(input, output); |
| 1796 | } |
| 1797 | }, |
| 1798 | }; |
| 1799 | |
| 1800 | // Test taken from |
| 1801 | // https://github.com/nodejs/node/blob/ef6b9ffc8dfc7b2a395c864d2729a0ce1be9ef18/test/parallel/test-zlib-close-after-write.js |
| 1802 | export const closeAfterWrite = { |
| 1803 | async test() { |
| 1804 | const { promise, resolve } = Promise.withResolvers(); |
| 1805 | zlib.gzip('hello', (err, out) => { |
| 1806 | assert.ifError(err); |
| 1807 | |
| 1808 | const unzip = zlib.createGunzip(); |
| 1809 | unzip.write(out); |
| 1810 | unzip.close(resolve); |
| 1811 | }); |
| 1812 | await promise; |
| 1813 | }, |
| 1814 | }; |
| 1815 | |
| 1816 | export const inflateSyncTest = { |
| 1817 | test() { |
| 1818 | const BIG_DATA = 'horse'.repeat(50_000) + 'cow'.repeat(49_000); |
| 1819 | assert.strictEqual( |
| 1820 | zlib.inflateSync(zlib.deflateSync(BIG_DATA)).toString(), |
| 1821 | BIG_DATA |
| 1822 | ); |
| 1823 | |
| 1824 | assert.throws( |
| 1825 | () => zlib.inflateSync('garbage data'), |
| 1826 | new Error('incorrect header check') |
| 1827 | ); |
| 1828 | |
| 1829 | assert.strictEqual( |
| 1830 | zlib |
| 1831 | .inflateSync(Buffer.from('OE9LyixKUUiCEQAmfgUk', 'base64'), { |
| 1832 | windowBits: 11, |
| 1833 | level: 4, |
| 1834 | }) |
| 1835 | .toString(), |
| 1836 | 'bird bird bird' |
| 1837 | ); |
| 1838 | }, |
| 1839 | }; |
| 1840 | |
| 1841 | export const zipBombTest = { |
| 1842 | test() { |
| 1843 | // 225 bytes (raw) |
| 1844 | const ZLIB_BOMB_3 = |
| 1845 | 'eNqruPX2jqHeUkaJtov/25xEzoho9fL6aF70yPBZvUBsqZPmzHO+F253euVbWScs28QQ2LG' + |
| 1846 | 'CpdBc8ef84mkfpubvs6n/Z/9vrk9b4oy4ffG2ujp8DAwMB074/p97traX8fCt43fPvbMDCh' + |
| 1847 | 'mUtZ/2/rw58MHbhNLblkCBhC96ZuZ1Z+xY5vn3P5/LDBRxy3/6NTF7G+udupj/d4r5gSISe' + |
| 1848 | '6sPAikGD0EeINkgBuZkQDgSyJxRmVGZUZlRmVGZURlCMpZRcVE/bjGdi/fzr+O/n/P32XWD' + |
| 1849 | 'nyxJaZvuAwDXRDs+'; |
| 1850 | |
| 1851 | // 1799 bytes |
| 1852 | const zlib_bomb_2 = zlib.inflateSync(Buffer.from(ZLIB_BOMB_3, 'base64')); |
| 1853 | |
| 1854 | // ~ 1MB |
| 1855 | const zlib_bomb_1 = zlib.inflateSync(zlib_bomb_2); |
| 1856 | |
| 1857 | // Would be 1 GB, if we let it |
| 1858 | assert.throws( |
| 1859 | () => zlib.inflateSync(zlib_bomb_1), |
| 1860 | new RangeError('Memory limit exceeded') |
| 1861 | ); |
| 1862 | }, |
| 1863 | }; |
| 1864 | |
| 1865 | export const deflateSyncTest = { |
| 1866 | test() { |
| 1867 | function maskOsId(buf) { |
| 1868 | // Clear the OS ID byte in gzip, which varies based on the platform used to run the tests |
| 1869 | return buf.fill(0x0, 9, 10); |
| 1870 | } |
| 1871 | |
| 1872 | assert.strictEqual( |
| 1873 | zlib |
| 1874 | .deflateSync('bird bird bird', { windowBits: 11, level: 4 }) |
| 1875 | .toString('base64'), |
| 1876 | 'OE9LyixKUUiCEQAmfgUk' |
| 1877 | ); |
| 1878 | |
| 1879 | assert.deepStrictEqual( |
| 1880 | maskOsId( |
| 1881 | zlib.gzipSync('water, water, everywhere, nor any drop to drink') |
| 1882 | ).toString('base64'), |
| 1883 | 'H4sIAAAAAAAAACtPLEkt0lEoh1CpZalFleUZqUWpOgp5+UUKiXmVCilF+QUKJfkKKUWZedkAqpLyPC8AAAA=' |
| 1884 | ); |
| 1885 | }, |
| 1886 | }; |
| 1887 | |
| 1888 | // Tests are taken from: |
| 1889 | // https://github.com/nodejs/node/blob/5949e169bd886fcc965172f671671b47ac07d2e2/test/parallel/test-zlib-premature-end.js |
| 1890 | export const prematureEnd = { |
| 1891 | async test() { |
| 1892 | const input = '0123456789'.repeat(4); |
| 1893 | |
| 1894 | for (const [compress, decompressor] of [ |
| 1895 | [zlib.deflateRawSync, zlib.createInflateRaw], |
| 1896 | [zlib.deflateSync, zlib.createInflate], |
| 1897 | [zlib.brotliCompressSync, zlib.createBrotliDecompress], |
| 1898 | ]) { |
| 1899 | const compressed = compress(input); |
| 1900 | const trailingData = Buffer.from('not valid compressed data'); |
| 1901 | |
| 1902 | for (const variant of [ |
| 1903 | (stream) => { |
| 1904 | stream.end(compressed); |
| 1905 | }, |
| 1906 | (stream) => { |
| 1907 | stream.write(compressed); |
| 1908 | stream.write(trailingData); |
| 1909 | }, |
| 1910 | (stream) => { |
| 1911 | stream.write(compressed); |
| 1912 | stream.end(trailingData); |
| 1913 | }, |
| 1914 | (stream) => { |
| 1915 | stream.write(Buffer.concat([compressed, trailingData])); |
| 1916 | }, |
| 1917 | (stream) => { |
| 1918 | stream.end(Buffer.concat([compressed, trailingData])); |
| 1919 | }, |
| 1920 | ]) { |
| 1921 | let output = ''; |
| 1922 | const { promise, resolve, reject } = Promise.withResolvers(); |
| 1923 | const stream = decompressor(); |
| 1924 | stream.setEncoding('utf8'); |
| 1925 | stream |
| 1926 | .on('data', (chunk) => (output += chunk)) |
| 1927 | .on('end', () => { |
| 1928 | assert.strictEqual(output, input); |
| 1929 | assert.strictEqual(stream.bytesWritten, compressed.length); |
| 1930 | resolve(); |
| 1931 | }) |
| 1932 | .on('error', reject); |
| 1933 | variant(stream); |
| 1934 | await promise; |
| 1935 | } |
| 1936 | } |
| 1937 | }, |
| 1938 | }; |
| 1939 | |
| 1940 | // Tests are taken from: |
| 1941 | // https://github.com/nodejs/node/blob/5949e169bd886fcc965172f671671b47ac07d2e2/test/parallel/test-zlib-write-after-flush.js |
| 1942 | export const writeAfterFlush = { |
| 1943 | async test() { |
| 1944 | for (const [createCompress, createDecompress] of [ |
| 1945 | [zlib.createGzip, zlib.createGunzip], |
| 1946 | [zlib.createBrotliCompress, zlib.createBrotliDecompress], |
| 1947 | ]) { |
| 1948 | const { promise, resolve, reject } = Promise.withResolvers(); |
| 1949 | const gzip = createCompress(); |
| 1950 | const gunz = createDecompress(); |
| 1951 | |
| 1952 | gzip.pipe(gunz); |
| 1953 | |
| 1954 | let output = ''; |
| 1955 | const input = 'A line of data\n'; |
| 1956 | gunz.setEncoding('utf8'); |
| 1957 | gunz |
| 1958 | .on('error', reject) |
| 1959 | .on('data', (c) => (output += c)) |
| 1960 | .on('end', resolve); |
| 1961 | |
| 1962 | // Make sure that flush/write doesn't trigger an assert failure |
| 1963 | gzip.flush(); |
| 1964 | gzip.write(input); |
| 1965 | gzip.end(); |
| 1966 | gunz.read(0); |
| 1967 | await promise; |
| 1968 | assert.strictEqual(output, input); |
| 1969 | } |
| 1970 | }, |
| 1971 | }; |
| 1972 | |
| 1973 | // Tests are taken from: |
| 1974 | // https://github.com/nodejs/node/blob/5949e169bd886fcc965172f671671b47ac07d2e2/test/parallel/test-zlib-destroy-pipe.js |
| 1975 | export const destroyPipe = { |
| 1976 | async test() { |
| 1977 | const ts = zlib.createGzip(); |
| 1978 | const { promise, resolve } = Promise.withResolvers(); |
| 1979 | |
| 1980 | const ws = new Writable({ |
| 1981 | write: (chunk, enc, cb) => { |
| 1982 | queueMicrotask(cb); |
| 1983 | ts.destroy(); |
| 1984 | resolve(); |
| 1985 | }, |
| 1986 | }); |
| 1987 | |
| 1988 | const buf = Buffer.allocUnsafe(1024 * 1024 * 20); |
| 1989 | ts.end(buf); |
| 1990 | ts.pipe(ws); |
| 1991 | await promise; |
| 1992 | }, |
| 1993 | }; |
| 1994 | |
| 1995 | // Tests are taken from: |
| 1996 | // https://github.com/nodejs/node/blob/5949e169bd886fcc965172f671671b47ac07d2e2/test/parallel/test-zlib-write-after-end.js |
| 1997 | export const writeAfterEnd = { |
| 1998 | async test() { |
| 1999 | const { promise, resolve } = Promise.withResolvers(); |
| 2000 | const data = zlib.deflateRawSync('Welcome'); |
| 2001 | const inflate = zlib.createInflateRaw(); |
| 2002 | const writeCallback = mock.fn(); |
| 2003 | inflate.resume(); |
| 2004 | inflate.write(data, writeCallback); |
| 2005 | inflate.write(Buffer.from([0x00]), writeCallback); |
| 2006 | inflate.write(Buffer.from([0x00]), writeCallback); |
| 2007 | inflate.flush(resolve); |
| 2008 | await promise; |
| 2009 | assert.strictEqual(writeCallback.mock.callCount(), 3); |
| 2010 | }, |
| 2011 | }; |
| 2012 | |
| 2013 | // Tests are taken from: |
| 2014 | // https://github.com/nodejs/node/blob/5949e169bd886fcc965172f671671b47ac07d2e2/test/parallel/test-zlib-convenience-methods.js |
| 2015 | export const convenienceMethods = { |
| 2016 | async test() { |
| 2017 | // Must be a multiple of 4 characters in total to test all ArrayBufferView |
| 2018 | // types. |
| 2019 | const expectStr = 'blah'.repeat(8); |
| 2020 | const expectBuf = Buffer.from(expectStr); |
| 2021 | |
| 2022 | const opts = { |
| 2023 | level: 9, |
| 2024 | chunkSize: 1024, |
| 2025 | }; |
| 2026 | |
| 2027 | const optsInfo = { |
| 2028 | info: true, |
| 2029 | }; |
| 2030 | |
| 2031 | for (const [type, expect] of [ |
| 2032 | ['string', expectStr], |
| 2033 | ['Buffer', expectBuf], |
| 2034 | ]) { |
| 2035 | for (const method of [ |
| 2036 | ['gzip', 'gunzip', 'Gzip', 'Gunzip'], |
| 2037 | ['gzip', 'unzip', 'Gzip', 'Unzip'], |
| 2038 | ['deflate', 'inflate', 'Deflate', 'Inflate'], |
| 2039 | ['deflateRaw', 'inflateRaw', 'DeflateRaw', 'InflateRaw'], |
| 2040 | [ |
| 2041 | 'brotliCompress', |
| 2042 | 'brotliDecompress', |
| 2043 | 'BrotliCompress', |
| 2044 | 'BrotliDecompress', |
| 2045 | ], |
| 2046 | ]) { |
| 2047 | { |
| 2048 | const { promise, resolve } = Promise.withResolvers(); |
| 2049 | zlib[method[0]](expect, opts, (err, result) => { |
| 2050 | assert.ifError(err); |
| 2051 | zlib[method[1]](result, opts, (err, result) => { |
| 2052 | assert.ifError(err); |
| 2053 | assert.strictEqual( |
| 2054 | result.toString(), |
| 2055 | expectStr, |
| 2056 | `Should get original string after ${method[0]}/` + |
| 2057 | `${method[1]} ${type} with options.` |
| 2058 | ); |
| 2059 | resolve(); |
| 2060 | }); |
| 2061 | }); |
| 2062 | await promise; |
| 2063 | } |
| 2064 | |
| 2065 | { |
| 2066 | const { promise, resolve } = Promise.withResolvers(); |
| 2067 | zlib[method[0]](expect, (err, result) => { |
| 2068 | assert.ifError(err); |
| 2069 | zlib[method[1]](result, (err, result) => { |
| 2070 | assert.ifError(err); |
| 2071 | assert.strictEqual( |
| 2072 | result.toString(), |
| 2073 | expectStr, |
| 2074 | `Should get original string after ${method[0]}/` + |
| 2075 | `${method[1]} ${type} without options.` |
| 2076 | ); |
| 2077 | resolve(); |
| 2078 | }); |
| 2079 | }); |
| 2080 | await promise; |
| 2081 | } |
| 2082 | |
| 2083 | { |
| 2084 | const { promise, resolve } = Promise.withResolvers(); |
| 2085 | zlib[method[0]](expect, optsInfo, (err, result) => { |
| 2086 | assert.ifError(err); |
| 2087 | |
| 2088 | const compressed = result.buffer; |
| 2089 | zlib[method[1]](compressed, optsInfo, (err, result) => { |
| 2090 | assert.ifError(err); |
| 2091 | assert.strictEqual( |
| 2092 | result.buffer.toString(), |
| 2093 | expectStr, |
| 2094 | `Should get original string after ${method[0]}/` + |
| 2095 | `${method[1]} ${type} with info option.` |
| 2096 | ); |
| 2097 | resolve(); |
| 2098 | }); |
| 2099 | }); |
| 2100 | await promise; |
| 2101 | } |
| 2102 | |
| 2103 | { |
| 2104 | const compressed = zlib[`${method[0]}Sync`](expect, opts); |
| 2105 | const decompressed = zlib[`${method[1]}Sync`](compressed, opts); |
| 2106 | assert.strictEqual( |
| 2107 | decompressed.toString(), |
| 2108 | expectStr, |
| 2109 | `Should get original string after ${method[0]}Sync/` + |
| 2110 | `${method[1]}Sync ${type} with options.` |
| 2111 | ); |
| 2112 | } |
| 2113 | |
| 2114 | { |
| 2115 | const compressed = zlib[`${method[0]}Sync`](expect); |
| 2116 | const decompressed = zlib[`${method[1]}Sync`](compressed); |
| 2117 | assert.strictEqual( |
| 2118 | decompressed.toString(), |
| 2119 | expectStr, |
| 2120 | `Should get original string after ${method[0]}Sync/` + |
| 2121 | `${method[1]}Sync ${type} without options.` |
| 2122 | ); |
| 2123 | } |
| 2124 | |
| 2125 | { |
| 2126 | const compressed = zlib[`${method[0]}Sync`](expect, optsInfo); |
| 2127 | const decompressed = zlib[`${method[1]}Sync`]( |
| 2128 | compressed.buffer, |
| 2129 | optsInfo |
| 2130 | ); |
| 2131 | assert.strictEqual( |
| 2132 | decompressed.buffer.toString(), |
| 2133 | expectStr, |
| 2134 | `Should get original string after ${method[0]}Sync/` + |
| 2135 | `${method[1]}Sync ${type} without options.` |
| 2136 | ); |
| 2137 | assert.ok( |
| 2138 | decompressed.engine instanceof zlib[method[3]], |
| 2139 | `Should get engine ${method[3]} after ${method[0]} ` + |
| 2140 | `${type} with info option.` |
| 2141 | ); |
| 2142 | } |
| 2143 | } |
| 2144 | } |
| 2145 | |
| 2146 | assert.throws(() => zlib.gzip('abc'), { |
| 2147 | code: 'ERR_INVALID_ARG_TYPE', |
| 2148 | name: 'TypeError', |
| 2149 | message: |
| 2150 | 'The "callback" argument must be of type function. ' + |
| 2151 | 'Received undefined', |
| 2152 | }); |
| 2153 | }, |
| 2154 | }; |
| 2155 | |
| 2156 | // Test taken from |
| 2157 | // https://github.com/nodejs/node/blob/2bd6a57b7b934afcaf437a90e2abebcb79c13acf/test/parallel/test-zlib-brotli-from-string.js |
| 2158 | export const brotliFromString = { |
| 2159 | async test() { |
| 2160 | const inputString = |
| 2161 | 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli' + |
| 2162 | 't. Morbi faucibus, purus at gravida dictum, libero arcu ' + |
| 2163 | 'convallis lacus, in commodo libero metus eu nisi. Nullam' + |
| 2164 | ' commodo, neque nec porta placerat, nisi est fermentum a' + |
| 2165 | 'ugue, vitae gravida tellus sapien sit amet tellus. Aenea' + |
| 2166 | 'n non diam orci. Proin quis elit turpis. Suspendisse non' + |
| 2167 | ' diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu' + |
| 2168 | 'm arcu mi, sodales non suscipit id, ultrices ut massa. S' + |
| 2169 | 'ed ac sem sit amet arcu malesuada fermentum. Nunc sed. '; |
| 2170 | const compressedString = |
| 2171 | 'G/gBQBwHdky2aHV5KK9Snf05//1pPdmNw/7232fnIm1IB' + |
| 2172 | 'K1AA8RsN8OB8Nb7Lpgk3UWWUlzQXZyHQeBBbXMTQXC1j7' + |
| 2173 | 'wg3LJs9LqOGHRH2bj/a2iCTLLx8hBOyTqgoVuD1e+Qqdn' + |
| 2174 | 'f1rkUNyrWq6LtOhWgxP3QUwdhKGdZm3rJWaDDBV7+pDk1' + |
| 2175 | 'MIkrmjp4ma2xVi5MsgJScA3tP1I7mXeby6MELozrwoBQD' + |
| 2176 | 'mVTnEAicZNj4lkGqntJe2qSnGyeMmcFgraK94vCg/4iLu' + |
| 2177 | 'Tw5RhKhnVY++dZ6niUBmRqIutsjf5TzwF5iAg8a9UkjF5' + |
| 2178 | '2eZ0tB2vo6v8SqVfNMkBmmhxr0NT9LkYF69aEjlYzj7IE' + |
| 2179 | 'KmEUQf1HBogRYhFIt4ymRNEgHAIzOyNEsQM='; |
| 2180 | |
| 2181 | { |
| 2182 | const { promise, resolve } = Promise.withResolvers(); |
| 2183 | |
| 2184 | zlib.brotliCompress(inputString, (err, buffer) => { |
| 2185 | assert.ifError(err); |
| 2186 | assert(inputString.length > buffer.length); |
| 2187 | |
| 2188 | zlib.brotliDecompress(buffer, (err, buffer) => { |
| 2189 | assert.ifError(err); |
| 2190 | assert.strictEqual(buffer.toString(), inputString); |
| 2191 | resolve(); |
| 2192 | }); |
| 2193 | }); |
| 2194 | await promise; |
| 2195 | } |
| 2196 | |
| 2197 | { |
| 2198 | const { promise, resolve } = Promise.withResolvers(); |
| 2199 | const buffer = Buffer.from(compressedString, 'base64'); |
| 2200 | zlib.brotliDecompress(buffer, (err, buffer) => { |
| 2201 | assert.ifError(err); |
| 2202 | assert.strictEqual(buffer.toString(), inputString); |
| 2203 | resolve(); |
| 2204 | }); |
| 2205 | |
| 2206 | await promise; |
| 2207 | } |
| 2208 | }, |
| 2209 | }; |
| 2210 | |
| 2211 | // Test taken from |
| 2212 | // https://github.com/nodejs/node/blob/26eb062a9b9c0ae8cee9cb5c378e43bca363207c/test/parallel/test-zlib-maxOutputLength.js |
| 2213 | export const maxOutputLength = { |
| 2214 | async test() { |
| 2215 | const encoded = Buffer.from('G38A+CXCIrFAIAM=', 'base64'); |
| 2216 | |
| 2217 | // Async |
| 2218 | { |
| 2219 | const { promise, resolve } = Promise.withResolvers(); |
| 2220 | zlib.brotliDecompress(encoded, { maxOutputLength: 64 }, (err) => { |
| 2221 | // TODO(soon): Make error the same as NodeJS |
| 2222 | assert.match(err.message, /Memory limit exceeded/); |
| 2223 | resolve(); |
| 2224 | }); |
| 2225 | await promise; |
| 2226 | } |
| 2227 | |
| 2228 | // Sync |
| 2229 | assert.throws(function () { |
| 2230 | zlib.brotliDecompressSync(encoded, { maxOutputLength: 64 }); |
| 2231 | }, RangeError); |
| 2232 | |
| 2233 | // Async |
| 2234 | { |
| 2235 | const { promise, resolve } = Promise.withResolvers(); |
| 2236 | zlib.brotliDecompress(encoded, { maxOutputLength: 256 }, function (err) { |
| 2237 | assert.strictEqual(err, null); |
| 2238 | resolve(); |
| 2239 | }); |
| 2240 | |
| 2241 | await promise; |
| 2242 | } |
| 2243 | |
| 2244 | // Sync |
| 2245 | zlib.brotliDecompressSync(encoded, { maxOutputLength: 256 }); |
| 2246 | }, |
| 2247 | }; |
| 2248 | |
| 2249 | // Test for maxOutputLength: 0 - should throw immediately instead of infinite loop |
| 2250 | export const maxOutputLengthZero = { |
| 2251 | async test() { |
| 2252 | const expectedError = { |
| 2253 | name: 'RangeError', |
| 2254 | message: |
| 2255 | /The value of "options\.maxOutputLength" is out of range\. It must be >= 1/, |
| 2256 | }; |
| 2257 | |
| 2258 | // Sync zlib - deflateSync with maxOutputLength: 0 should throw |
| 2259 | assert.throws( |
| 2260 | () => zlib.deflateSync('data', { maxOutputLength: 0 }), |
| 2261 | expectedError |
| 2262 | ); |
| 2263 | |
| 2264 | // Sync zlib - inflateSync with maxOutputLength: 0 should throw |
| 2265 | const compressed = zlib.deflateSync('data'); |
| 2266 | assert.throws( |
| 2267 | () => zlib.inflateSync(compressed, { maxOutputLength: 0 }), |
| 2268 | expectedError |
| 2269 | ); |
| 2270 | |
| 2271 | // Sync brotli - brotliCompressSync with maxOutputLength: 0 should throw |
| 2272 | assert.throws( |
| 2273 | () => zlib.brotliCompressSync('data', { maxOutputLength: 0 }), |
| 2274 | expectedError |
| 2275 | ); |
| 2276 | |
| 2277 | // Sync brotli - brotliDecompressSync with maxOutputLength: 0 should throw |
| 2278 | const brotliCompressed = zlib.brotliCompressSync('data'); |
| 2279 | assert.throws( |
| 2280 | () => zlib.brotliDecompressSync(brotliCompressed, { maxOutputLength: 0 }), |
| 2281 | expectedError |
| 2282 | ); |
| 2283 | |
| 2284 | // Async zlib - deflate with maxOutputLength: 0 should error |
| 2285 | { |
| 2286 | const { promise, resolve } = Promise.withResolvers(); |
| 2287 | zlib.deflate('data', { maxOutputLength: 0 }, (err) => { |
| 2288 | assert.match(err.message, expectedError.message); |
| 2289 | resolve(); |
| 2290 | }); |
| 2291 | await promise; |
| 2292 | } |
| 2293 | |
| 2294 | // Async brotli - brotliCompress with maxOutputLength: 0 should error |
| 2295 | { |
| 2296 | const { promise, resolve } = Promise.withResolvers(); |
| 2297 | zlib.brotliCompress('data', { maxOutputLength: 0 }, (err) => { |
| 2298 | assert.match(err.message, expectedError.message); |
| 2299 | resolve(); |
| 2300 | }); |
| 2301 | await promise; |
| 2302 | } |
| 2303 | }, |
| 2304 | }; |
| 2305 | |
| 2306 | // Test taken from |
| 2307 | // https://github.com/nodejs/node/blob/24302c9fe94e1dd755ac8a8cc1f6aa4444f75cb3/test/parallel/test-zlib-invalid-arg-value-brotli-compress.js |
| 2308 | export const invalidArgValueBrotliCompress = { |
| 2309 | test() { |
| 2310 | const opts = { |
| 2311 | params: { |
| 2312 | [zlib.constants.BROTLI_PARAM_MODE]: 'lol', |
| 2313 | }, |
| 2314 | }; |
| 2315 | |
| 2316 | // TODO(soon): Node's test invokes BrotliCompress without new, but we barf if you try that. |
| 2317 | assert.throws(() => new zlib.BrotliCompress(opts), { |
| 2318 | code: 'ERR_INVALID_ARG_TYPE', |
| 2319 | }); |
| 2320 | }, |
| 2321 | }; |
| 2322 | |
| 2323 | // Test taken from |
| 2324 | // https://github.com/nodejs/node/blob/24302c9fe94e1dd755ac8a8cc1f6aa4444f75cb3/test/parallel/test-zlib-brotli-flush.js |
| 2325 | export const brotliFlush = { |
| 2326 | async test() { |
| 2327 | const deflater = new zlib.BrotliCompress(); |
| 2328 | |
| 2329 | const chunk = Buffer.from('/9j/4AAQSkZJRgABAQEASA==', 'base64'); |
| 2330 | const expectedFull = Buffer.from('iweA/9j/4AAQSkZJRgABAQEASA==', 'base64'); |
| 2331 | let actualFull; |
| 2332 | |
| 2333 | { |
| 2334 | const { promise, resolve } = Promise.withResolvers(); |
| 2335 | deflater.write(chunk, function () { |
| 2336 | deflater.flush(function () { |
| 2337 | const bufs = []; |
| 2338 | let buf; |
| 2339 | while ((buf = deflater.read()) !== null) bufs.push(buf); |
| 2340 | actualFull = Buffer.concat(bufs); |
| 2341 | resolve(); |
| 2342 | }); |
| 2343 | }); |
| 2344 | |
| 2345 | await promise; |
| 2346 | } |
| 2347 | assert.deepStrictEqual(actualFull, expectedFull); |
| 2348 | }, |
| 2349 | }; |
| 2350 | |
| 2351 | // Test taken from |
| 2352 | // https://github.com/nodejs/node/blob/24302c9fe94e1dd755ac8a8cc1f6aa4444f75cb3/test/parallel/test-zlib-brotli.js |
| 2353 | export const brotli = { |
| 2354 | async test() { |
| 2355 | { |
| 2356 | const sampleBuffer = Buffer.from(PSS_VECTORS_JSON); |
| 2357 | |
| 2358 | // Test setting the quality parameter at stream creation: |
| 2359 | const sizes = []; |
| 2360 | for ( |
| 2361 | let quality = zlib.constants.BROTLI_MIN_QUALITY; |
| 2362 | quality <= zlib.constants.BROTLI_MAX_QUALITY; |
| 2363 | quality++ |
| 2364 | ) { |
| 2365 | const encoded = zlib.brotliCompressSync(sampleBuffer, { |
| 2366 | params: { |
| 2367 | [zlib.constants.BROTLI_PARAM_QUALITY]: quality, |
| 2368 | }, |
| 2369 | }); |
| 2370 | sizes.push(encoded.length); |
| 2371 | } |
| 2372 | |
| 2373 | // Increasing quality should roughly correspond to decreasing compressed size: |
| 2374 | for (let i = 0; i < sizes.length - 1; i++) { |
| 2375 | assert(sizes[i + 1] <= sizes[i] * 1.05, sizes); // 5 % margin of error. |
| 2376 | } |
| 2377 | assert(sizes[0] > sizes[sizes.length - 1], sizes); |
| 2378 | } |
| 2379 | |
| 2380 | { |
| 2381 | // Test that setting out-of-bounds option values or keys fails. |
| 2382 | assert.throws( |
| 2383 | () => { |
| 2384 | zlib.createBrotliCompress({ |
| 2385 | params: { |
| 2386 | 10000: 0, |
| 2387 | }, |
| 2388 | }); |
| 2389 | }, |
| 2390 | { |
| 2391 | code: 'ERR_BROTLI_INVALID_PARAM', |
| 2392 | name: 'RangeError', |
| 2393 | message: '10000 is not a valid Brotli parameter', |
| 2394 | } |
| 2395 | ); |
| 2396 | |
| 2397 | // Test that accidentally using duplicate keys fails. |
| 2398 | assert.throws( |
| 2399 | () => { |
| 2400 | zlib.createBrotliCompress({ |
| 2401 | params: { |
| 2402 | 0: 0, |
| 2403 | '00': 0, |
| 2404 | }, |
| 2405 | }); |
| 2406 | }, |
| 2407 | { |
| 2408 | code: 'ERR_BROTLI_INVALID_PARAM', |
| 2409 | name: 'RangeError', |
| 2410 | message: '00 is not a valid Brotli parameter', |
| 2411 | } |
| 2412 | ); |
| 2413 | |
| 2414 | { |
| 2415 | assert.throws( |
| 2416 | () => { |
| 2417 | zlib.createBrotliCompress({ |
| 2418 | params: { |
| 2419 | // This is a boolean flag |
| 2420 | [zlib.constants.BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING]: |
| 2421 | 42, |
| 2422 | }, |
| 2423 | }); |
| 2424 | }, |
| 2425 | { |
| 2426 | code: 'ERR_ZLIB_INITIALIZATION_FAILED', |
| 2427 | name: 'Error', |
| 2428 | message: 'Initialization failed', |
| 2429 | } |
| 2430 | ); |
| 2431 | } |
| 2432 | |
| 2433 | { |
| 2434 | // Test options.flush range |
| 2435 | // TODO(soon): Use the same code and message as NodeJS |
| 2436 | assert.throws( |
| 2437 | () => { |
| 2438 | zlib.brotliCompressSync('', { flush: zlib.constants.Z_FINISH }); |
| 2439 | }, |
| 2440 | { |
| 2441 | //code: 'ERR_OUT_OF_RANGE', |
| 2442 | name: 'RangeError', |
| 2443 | message: |
| 2444 | 'The value of "options.flush" is out of range. It must be >= 0 ' + |
| 2445 | 'and <= 3. Received 4', |
| 2446 | } |
| 2447 | ); |
| 2448 | |
| 2449 | assert.throws( |
| 2450 | () => { |
| 2451 | zlib.brotliCompressSync('', { |
| 2452 | finishFlush: zlib.constants.Z_FINISH, |
| 2453 | }); |
| 2454 | }, |
| 2455 | { |
| 2456 | //code: 'ERR_OUT_OF_RANGE', |
| 2457 | name: 'RangeError', |
| 2458 | message: |
| 2459 | 'The value of "options.finishFlush" is out of range. It must be ' + |
| 2460 | '>= 0 and <= 3. Received 4', |
| 2461 | } |
| 2462 | ); |
| 2463 | } |
| 2464 | } |
| 2465 | }, |
| 2466 | }; |
| 2467 | |
| 2468 | // Tests are taken from: |
| 2469 | // https://github.com/nodejs/node/blob/2ef4b15604082abfd7aa26a4619a46802258ff3c/test/parallel/test-zlib-random-byte-pipes.js |
| 2470 | export const zlibRandomBytePipes = { |
| 2471 | async test() { |
| 2472 | // Emit random bytes, and keep a shasum |
| 2473 | class RandomReadStream extends Stream { |
| 2474 | constructor(opt) { |
| 2475 | super(); |
| 2476 | |
| 2477 | this.readable = true; |
| 2478 | this._paused = false; |
| 2479 | this._processing = false; |
| 2480 | this._hasher = crypto.createHash('sha1'); |
| 2481 | opt ??= {}; |
| 2482 | |
| 2483 | // base block size. |
| 2484 | opt.block ??= 256 * 1024; |
| 2485 | // Total number of bytes to emit |
| 2486 | opt.total ??= 256 * 1024 * 1024; |
| 2487 | this._remaining = opt.total; |
| 2488 | // How variable to make the block sizes |
| 2489 | opt.jitter ??= 1024; |
| 2490 | this._opt = opt; |
| 2491 | this._process = this._process.bind(this); |
| 2492 | queueMicrotask(this._process); |
| 2493 | } |
| 2494 | |
| 2495 | pause() { |
| 2496 | this._paused = true; |
| 2497 | this.emit('pause'); |
| 2498 | } |
| 2499 | |
| 2500 | resume() { |
| 2501 | this._paused = false; |
| 2502 | this.emit('resume'); |
| 2503 | this._process(); |
| 2504 | } |
| 2505 | |
| 2506 | _process() { |
| 2507 | if (this._processing) return; |
| 2508 | if (this._paused) return; |
| 2509 | this._processing = true; |
| 2510 | |
| 2511 | if (!this._remaining) { |
| 2512 | this._hash = this._hasher.digest('hex').toLowerCase().trim(); |
| 2513 | this._processing = false; |
| 2514 | |
| 2515 | this.emit('end'); |
| 2516 | return; |
| 2517 | } |
| 2518 | |
| 2519 | // Figure out how many bytes to output |
| 2520 | // if finished, then just emit end. |
| 2521 | let block = this._opt.block; |
| 2522 | const jitter = this._opt.jitter; |
| 2523 | if (jitter) { |
| 2524 | block += Math.ceil(Math.random() * jitter - jitter / 2); |
| 2525 | } |
| 2526 | block = Math.min(block, this._remaining); |
| 2527 | const buf = Buffer.allocUnsafe(block); |
| 2528 | for (let i = 0; i < block; i++) { |
| 2529 | buf[i] = Math.random() * 256; |
| 2530 | } |
| 2531 | |
| 2532 | this._hasher.update(buf); |
| 2533 | this._remaining -= block; |
| 2534 | this._processing = false; |
| 2535 | this.emit('data', buf); |
| 2536 | queueMicrotask(this._process); |
| 2537 | } |
| 2538 | } |
| 2539 | |
| 2540 | // A filter that just verifies a shasum |
| 2541 | class HashStream extends Stream { |
| 2542 | constructor() { |
| 2543 | super(); |
| 2544 | this.readable = this.writable = true; |
| 2545 | this._hasher = crypto.createHash('sha1'); |
| 2546 | } |
| 2547 | |
| 2548 | write(c) { |
| 2549 | // Simulate the way that an fs.ReadStream returns false |
| 2550 | // on *every* write, only to resume a moment later. |
| 2551 | this._hasher.update(c); |
| 2552 | queueMicrotask(() => this.resume()); |
| 2553 | return false; |
| 2554 | } |
| 2555 | |
| 2556 | resume() { |
| 2557 | this.emit('resume'); |
| 2558 | queueMicrotask(() => this.emit('drain')); |
| 2559 | } |
| 2560 | |
| 2561 | end(c) { |
| 2562 | if (c) { |
| 2563 | this.write(c); |
| 2564 | } |
| 2565 | this._hash = this._hasher.digest('hex').toLowerCase().trim(); |
| 2566 | this.emit('data', this._hash); |
| 2567 | this.emit('end'); |
| 2568 | } |
| 2569 | } |
| 2570 | |
| 2571 | for (const [createCompress, createDecompress] of [ |
| 2572 | [zlib.createGzip, zlib.createGunzip], |
| 2573 | [zlib.createBrotliCompress, zlib.createBrotliDecompress], |
| 2574 | ]) { |
| 2575 | const { promise, resolve, reject } = Promise.withResolvers(); |
| 2576 | const inp = new RandomReadStream({ total: 1024, block: 256, jitter: 16 }); |
| 2577 | const out = new HashStream(); |
| 2578 | const gzip = createCompress(); |
| 2579 | const gunz = createDecompress(); |
| 2580 | |
| 2581 | inp.pipe(gzip).pipe(gunz).pipe(out); |
| 2582 | |
| 2583 | const onDataFn = mock.fn(); |
| 2584 | onDataFn.mock.mockImplementation((c) => { |
| 2585 | assert.strictEqual(c, inp._hash, `Hash '${c}' equals '${inp._hash}'.`); |
| 2586 | }); |
| 2587 | |
| 2588 | out.on('data', onDataFn).on('end', resolve).on('error', reject); |
| 2589 | |
| 2590 | await promise; |
| 2591 | |
| 2592 | assert.ok(onDataFn.mock.callCount() > 0, 'Should have called onData'); |
| 2593 | } |
| 2594 | }, |
| 2595 | }; |
| 2596 | |
| 2597 | // Tests are taken from: |
| 2598 | // https://github.com/nodejs/node/blob/2ef4b15604082abfd7aa26a4619a46802258ff3c/test/parallel/test-zlib-sync-no-event.js |
| 2599 | export const zlibSyncNoEvent = { |
| 2600 | async test() { |
| 2601 | const { promise, resolve, reject } = Promise.withResolvers(); |
| 2602 | const message = 'Come on, Fhqwhgads.'; |
| 2603 | const buffer = Buffer.from(message); |
| 2604 | |
| 2605 | const zipper = new zlib.Gzip(); |
| 2606 | zipper.on('close', reject); |
| 2607 | |
| 2608 | const zipped = zipper._processChunk(buffer, zlib.constants.Z_FINISH); |
| 2609 | |
| 2610 | const unzipper = new zlib.Gunzip(); |
| 2611 | unzipper.on('close', reject); |
| 2612 | |
| 2613 | const unzipped = unzipper._processChunk(zipped, zlib.constants.Z_FINISH); |
| 2614 | assert.notStrictEqual(zipped.toString(), message); |
| 2615 | assert.strictEqual(unzipped.toString(), message); |
| 2616 | |
| 2617 | queueMicrotask(resolve); |
| 2618 | |
| 2619 | await promise; |
| 2620 | }, |
| 2621 | }; |
| 2622 | |
| 2623 | // Tests are taken from: |
| 2624 | // https://github.com/nodejs/node/blob/2ef4b15604082abfd7aa26a4619a46802258ff3c/test/parallel/test-zlib-brotli-16GB.js |
| 2625 | export const zlibBrotli16GB = { |
| 2626 | async test() { |
| 2627 | const { promise, resolve } = Promise.withResolvers(); |
| 2628 | const content = |
| 2629 | 'cfffff7ff82700e2b14020f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c32200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bff3f'; |
| 2630 | const buf = Buffer.from(content, 'hex'); |
| 2631 | const decoder = zlib.createBrotliDecompress(); |
| 2632 | |
| 2633 | decoder.end(buf); |
| 2634 | |
| 2635 | queueMicrotask(() => { |
| 2636 | // There is only one chunk in the buffer |
| 2637 | assert.strictEqual( |
| 2638 | decoder._readableState.buffer.length, |
| 2639 | getDefaultHighWaterMark() / (16 * 1024) |
| 2640 | ); |
| 2641 | resolve(); |
| 2642 | }); |
| 2643 | |
| 2644 | await promise; |
| 2645 | }, |
| 2646 | }; |
| 2647 | |
| 2648 | // Large test data is added at the end of the file in order to make the test code itself more readable |
| 2649 | const PSS_VECTORS_JSON = `{ |
| 2650 | "example01": { |
| 2651 | "publicKey": [ |
| 2652 | "-----BEGIN PUBLIC KEY-----", |
| 2653 | "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClbkoOcBAXWJpRh9x+qEHRVvLs", |
| 2654 | "DjatUqRN/rHmH3rZkdjFEFb/7bFitMDyg6EqiKOU3/Umq3KRy7MHzqv84LHf1c2V", |
| 2655 | "CAltWyuLbfXWce9jd8CSHLI8Jwpw4lmOb/idGfEFrMLT8Ms18pKA4Thrb2TE7yLh", |
| 2656 | "4fINDOjP+yJJvZohNwIDAQAB", |
| 2657 | "-----END PUBLIC KEY-----" |
| 2658 | ], |
| 2659 | "tests": [ |
| 2660 | { |
| 2661 | "message": "cdc87da223d786df3b45e0bbbc721326d1ee2af806cc315475cc6f0d9c66e1b62371d45ce2392e1ac92844c310102f156a0d8d52c1f4c40ba3aa65095786cb769757a6563ba958fed0bcc984e8b517a3d5f515b23b8a41e74aa867693f90dfb061a6e86dfaaee64472c00e5f20945729cbebe77f06ce78e08f4098fba41f9d6193c0317e8b60d4b6084acb42d29e3808a3bc372d85e331170fcbf7cc72d0b71c296648b3a4d10f416295d0807aa625cab2744fd9ea8fd223c42537029828bd16be02546f130fd2e33b936d2676e08aed1b73318b750a0167d0", |
| 2662 | "salt": "dee959c7e06411361420ff80185ed57f3e6776af", |
| 2663 | "signature": "9074308fb598e9701b2294388e52f971faac2b60a5145af185df5287b5ed2887e57ce7fd44dc8634e407c8e0e4360bc226f3ec227f9d9e54638e8d31f5051215df6ebb9c2f9579aa77598a38f914b5b9c1bd83c4e2f9f382a0d0aa3542ffee65984a601bc69eb28deb27dca12c82c2d4c3f66cd500f1ff2b994d8a4e30cbb33c" |
| 2664 | }, |
| 2665 | { |
| 2666 | "message": "851384cdfe819c22ed6c4ccb30daeb5cf059bc8e1166b7e3530c4c233e2b5f8f71a1cca582d43ecc72b1bca16dfc7013226b9e", |
| 2667 | "salt": "ef2869fa40c346cb183dab3d7bffc98fd56df42d", |
| 2668 | "signature": "3ef7f46e831bf92b32274142a585ffcefbdca7b32ae90d10fb0f0c729984f04ef29a9df0780775ce43739b97838390db0a5505e63de927028d9d29b219ca2c4517832558a55d694a6d25b9dab66003c4cccd907802193be5170d26147d37b93590241be51c25055f47ef62752cfbe21418fafe98c22c4d4d47724fdb5669e843" |
| 2669 | }, |
| 2670 | { |
| 2671 | "message": "a4b159941761c40c6a82f2b80d1b94f5aa2654fd17e12d588864679b54cd04ef8bd03012be8dc37f4b83af7963faff0dfa225477437c48017ff2be8191cf3955fc07356eab3f322f7f620e21d254e5db4324279fe067e0910e2e81ca2cab31c745e67a54058eb50d993cdb9ed0b4d029c06d21a94ca661c3ce27fae1d6cb20f4564d66ce4767583d0e5f060215b59017be85ea848939127bd8c9c4d47b51056c031cf336f17c9980f3b8f5b9b6878e8b797aa43b882684333e17893fe9caa6aa299f7ed1a18ee2c54864b7b2b99b72618fb02574d139ef50f019c9eef416971338e7d470", |
| 2672 | "salt": "710b9c4747d800d4de87f12afdce6df18107cc77", |
| 2673 | "signature": "666026fba71bd3e7cf13157cc2c51a8e4aa684af9778f91849f34335d141c00154c4197621f9624a675b5abc22ee7d5baaffaae1c9baca2cc373b3f33e78e6143c395a91aa7faca664eb733afd14d8827259d99a7550faca501ef2b04e33c23aa51f4b9e8282efdb728cc0ab09405a91607c6369961bc8270d2d4f39fce612b1" |
| 2674 | }, |
| 2675 | { |
| 2676 | "message": "bc656747fa9eafb3f0", |
| 2677 | "salt": "056f00985de14d8ef5cea9e82f8c27bef720335e", |
| 2678 | "signature": "4609793b23e9d09362dc21bb47da0b4f3a7622649a47d464019b9aeafe53359c178c91cd58ba6bcb78be0346a7bc637f4b873d4bab38ee661f199634c547a1ad8442e03da015b136e543f7ab07c0c13e4225b8de8cce25d4f6eb8400f81f7e1833b7ee6e334d370964ca79fdb872b4d75223b5eeb08101591fb532d155a6de87" |
| 2679 | }, |
| 2680 | { |
| 2681 | "message": "b45581547e5427770c768e8b82b75564e0ea4e9c32594d6bff706544de0a8776c7a80b4576550eee1b2acabc7e8b7d3ef7bb5b03e462c11047eadd00629ae575480ac1470fe046f13a2bf5af17921dc4b0aa8b02bee6334911651d7f8525d10f32b51d33be520d3ddf5a709955a3dfe78283b9e0ab54046d150c177f037fdccc5be4ea5f68b5e5a38c9d7edcccc4975f455a6909b4", |
| 2682 | "salt": "80e70ff86a08de3ec60972b39b4fbfdcea67ae8e", |
| 2683 | "signature": "1d2aad221ca4d31ddf13509239019398e3d14b32dc34dc5af4aeaea3c095af73479cf0a45e5629635a53a018377615b16cb9b13b3e09d671eb71e387b8545c5960da5a64776e768e82b2c93583bf104c3fdb23512b7b4e89f633dd0063a530db4524b01c3f384c09310e315a79dcd3d684022a7f31c865a664e316978b759fad" |
| 2684 | }, |
| 2685 | { |
| 2686 | "message": "10aae9a0ab0b595d0841207b700d48d75faedde3b775cd6b4cc88ae06e4694ec74ba18f8520d4f5ea69cbbe7cc2beba43efdc10215ac4eb32dc302a1f53dc6c4352267e7936cfebf7c8d67035784a3909fa859c7b7b59b8e39c5c2349f1886b705a30267d402f7486ab4f58cad5d69adb17ab8cd0ce1caf5025af4ae24b1fb8794c6070cc09a51e2f9911311e3877d0044c71c57a993395008806b723ac38373d395481818528c1e7053739282053529510e935cd0fa77b8fa53cc2d474bd4fb3cc5c672d6ffdc90a00f9848712c4bcfe46c60573659b11e6457e861f0f604b6138d144f8ce4e2da73", |
| 2687 | "salt": "a8ab69dd801f0074c2a1fc60649836c616d99681", |
| 2688 | "signature": "2a34f6125e1f6b0bf971e84fbd41c632be8f2c2ace7de8b6926e31ff93e9af987fbc06e51e9be14f5198f91f3f953bd67da60a9df59764c3dc0fe08e1cbef0b75f868d10ad3fba749fef59fb6dac46a0d6e504369331586f58e4628f39aa278982543bc0eeb537dc61958019b394fb273f215858a0a01ac4d650b955c67f4c58" |
| 2689 | } |
| 2690 | ] |
| 2691 | }, |
| 2692 | "example10": { |
| 2693 | "publicKey": [ |
| 2694 | "-----BEGIN PUBLIC KEY-----", |
| 2695 | "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApd2GesTLAvkLlFfUjBSn", |
| 2696 | "cO+ZHFbDnA7GX9Ea+ok3zqV7m+esc7RcABdhW4LWIuMYdTtgJ8D9FXvhL4CQ/uKn", |
| 2697 | "rc0O73WfiLpJl8ekLVjJqhLLma4AH+UhwTu1QxRFqNWuT15MfpSKwifTYEBx8g5X", |
| 2698 | "fpBfvrFd+vBtHeWuYlPWOmohILMaXaXavJVQYA4g8n03OeJieSX+o8xQnyHf8E5u", |
| 2699 | "6kVJxUDWgJ/5MH7t6R//WHM9g4WiN9bTcFoz45GQCZIHDfet8TV89+NwDONmfeg/", |
| 2700 | "F7jfF3jbOB3OCctK0FilEQAac4GY7ifPVaE7dUU5kGWC7IsXS9WNXR89dnxhNyGu", |
| 2701 | "BQIDAQAB", |
| 2702 | "-----END PUBLIC KEY-----" |
| 2703 | ], |
| 2704 | "tests": [ |
| 2705 | { |
| 2706 | "message": "883177e5126b9be2d9a9680327d5370c6f26861f5820c43da67a3ad609", |
| 2707 | "salt": "04e215ee6ff934b9da70d7730c8734abfcecde89", |
| 2708 | "signature": "82c2b160093b8aa3c0f7522b19f87354066c77847abf2a9fce542d0e84e920c5afb49ffdfdace16560ee94a1369601148ebad7a0e151cf16331791a5727d05f21e74e7eb811440206935d744765a15e79f015cb66c532c87a6a05961c8bfad741a9a6657022894393e7223739796c02a77455d0f555b0ec01ddf259b6207fd0fd57614cef1a5573baaff4ec00069951659b85f24300a25160ca8522dc6e6727e57d019d7e63629b8fe5e89e25cc15beb3a647577559299280b9b28f79b0409000be25bbd96408ba3b43cc486184dd1c8e62553fa1af4040f60663de7f5e49c04388e257f1ce89c95dab48a315d9b66b1b7628233876ff2385230d070d07e1666" |
| 2709 | }, |
| 2710 | { |
| 2711 | "message": "dd670a01465868adc93f26131957a50c52fb777cdbaa30892c9e12361164ec13979d43048118e4445db87bee58dd987b3425d02071d8dbae80708b039dbb64dbd1de5657d9fed0c118a54143742e0ff3c87f74e45857647af3f79eb0a14c9d75ea9a1a04b7cf478a897a708fd988f48e801edb0b7039df8c23bb3c56f4e821ac", |
| 2712 | "salt": "8b2bdd4b40faf545c778ddf9bc1a49cb57f9b71b", |
| 2713 | "signature": "14ae35d9dd06ba92f7f3b897978aed7cd4bf5ff0b585a40bd46ce1b42cd2703053bb9044d64e813d8f96db2dd7007d10118f6f8f8496097ad75e1ff692341b2892ad55a633a1c55e7f0a0ad59a0e203a5b8278aec54dd8622e2831d87174f8caff43ee6c46445345d84a59659bfb92ecd4c818668695f34706f66828a89959637f2bf3e3251c24bdba4d4b7649da0022218b119c84e79a6527ec5b8a5f861c159952e23ec05e1e717346faefe8b1686825bd2b262fb2531066c0de09acde2e4231690728b5d85e115a2f6b92b79c25abc9bd9399ff8bcf825a52ea1f56ea76dd26f43baafa18bfa92a504cbd35699e26d1dcc5a2887385f3c63232f06f3244c3" |
| 2714 | }, |
| 2715 | { |
| 2716 | "message": "48b2b6a57a63c84cea859d65c668284b08d96bdcaabe252db0e4a96cb1bac6019341db6fbefb8d106b0e90eda6bcc6c6262f37e7ea9c7e5d226bd7df85ec5e71efff2f54c5db577ff729ff91b842491de2741d0c631607df586b905b23b91af13da12304bf83eca8a73e871ff9db", |
| 2717 | "salt": "4e96fc1b398f92b44671010c0dc3efd6e20c2d73", |
| 2718 | "signature": "6e3e4d7b6b15d2fb46013b8900aa5bbb3939cf2c095717987042026ee62c74c54cffd5d7d57efbbf950a0f5c574fa09d3fc1c9f513b05b4ff50dd8df7edfa20102854c35e592180119a70ce5b085182aa02d9ea2aa90d1df03f2daae885ba2f5d05afdac97476f06b93b5bc94a1a80aa9116c4d615f333b098892b25fface266f5db5a5a3bcc10a824ed55aad35b727834fb8c07da28fcf416a5d9b2224f1f8b442b36f91e456fdea2d7cfe3367268de0307a4c74e924159ed33393d5e0655531c77327b89821bdedf880161c78cd4196b5419f7acc3f13e5ebf161b6e7c6724716ca33b85c2e25640192ac2859651d50bde7eb976e51cec828b98b6563b86bb" |
| 2719 | }, |
| 2720 | { |
| 2721 | "message": "0b8777c7f839baf0a64bbbdbc5ce79755c57a205b845c174e2d2e90546a089c4e6ec8adffa23a7ea97bae6b65d782b82db5d2b5a56d22a29a05e7c4433e2b82a621abba90add05ce393fc48a840542451a", |
| 2722 | "salt": "c7cd698d84b65128d8835e3a8b1eb0e01cb541ec", |
| 2723 | "signature": "34047ff96c4dc0dc90b2d4ff59a1a361a4754b255d2ee0af7d8bf87c9bc9e7ddeede33934c63ca1c0e3d262cb145ef932a1f2c0a997aa6a34f8eaee7477d82ccf09095a6b8acad38d4eec9fb7eab7ad02da1d11d8e54c1825e55bf58c2a23234b902be124f9e9038a8f68fa45dab72f66e0945bf1d8bacc9044c6f07098c9fcec58a3aab100c805178155f030a124c450e5acbda47d0e4f10b80a23f803e774d023b0015c20b9f9bbe7c91296338d5ecb471cafb032007b67a60be5f69504a9f01abb3cb467b260e2bce860be8d95bf92c0c8e1496ed1e528593a4abb6df462dde8a0968dffe4683116857a232f5ebf6c85be238745ad0f38f767a5fdbf486fb" |
| 2724 | }, |
| 2725 | { |
| 2726 | "message": "f1036e008e71e964dadc9219ed30e17f06b4b68a955c16b312b1eddf028b74976bed6b3f6a63d4e77859243c9cccdc98016523abb02483b35591c33aad81213bb7c7bb1a470aabc10d44256c4d4559d916", |
| 2727 | "salt": "efa8bff96212b2f4a3f371a10d574152655f5dfb", |
| 2728 | "signature": "7e0935ea18f4d6c1d17ce82eb2b3836c55b384589ce19dfe743363ac9948d1f346b7bfddfe92efd78adb21faefc89ade42b10f374003fe122e67429a1cb8cbd1f8d9014564c44d120116f4990f1a6e38774c194bd1b8213286b077b0499d2e7b3f434ab12289c556684deed78131934bb3dd6537236f7c6f3dcb09d476be07721e37e1ceed9b2f7b406887bd53157305e1c8b4f84d733bc1e186fe06cc59b6edb8f4bd7ffefdf4f7ba9cfb9d570689b5a1a4109a746a690893db3799255a0cb9215d2d1cd490590e952e8c8786aa0011265252470c041dfbc3eec7c3cbf71c24869d115c0cb4a956f56d530b80ab589acfefc690751ddf36e8d383f83cedd2cc" |
| 2729 | }, |
| 2730 | { |
| 2731 | "message": "25f10895a87716c137450bb9519dfaa1f207faa942ea88abf71e9c17980085b555aebab76264ae2a3ab93c2d12981191ddac6fb5949eb36aee3c5da940f00752c916d94608fa7d97ba6a2915b688f20323d4e9d96801d89a72ab5892dc2117c07434fcf972e058cf8c41ca4b4ff554f7d5068ad3155fced0f3125bc04f9193378a8f5c4c3b8cb4dd6d1cc69d30ecca6eaa51e36a05730e9e342e855baf099defb8afd7", |
| 2732 | "salt": "ad8b1523703646224b660b550885917ca2d1df28", |
| 2733 | "signature": "6d3b5b87f67ea657af21f75441977d2180f91b2c5f692de82955696a686730d9b9778d970758ccb26071c2209ffbd6125be2e96ea81b67cb9b9308239fda17f7b2b64ecda096b6b935640a5a1cb42a9155b1c9ef7a633a02c59f0d6ee59b852c43b35029e73c940ff0410e8f114eed46bbd0fae165e42be2528a401c3b28fd818ef3232dca9f4d2a0f5166ec59c42396d6c11dbc1215a56fa17169db9575343ef34f9de32a49cdc3174922f229c23e18e45df9353119ec4319cedce7a17c64088c1f6f52be29634100b3919d38f3d1ed94e6891e66a73b8fb849f5874df59459e298c7bbce2eee782a195aa66fe2d0732b25e595f57d3e061b1fc3e4063bf98f" |
| 2734 | } |
| 2735 | ] |
| 2736 | } |
| 2737 | }`; |
| 2738 | |
| 2739 | export const compatFlagTest = { |
| 2740 | async test(_, env) { |
| 2741 | const waitForIt = async (test) => { |
| 2742 | const res = await env[test].fetch('http://example.org'); |
| 2743 | return await res.text(); |
| 2744 | }; |
| 2745 | const results = await Promise.allSettled([ |
| 2746 | waitForIt('compat'), |
| 2747 | waitForIt('compatv2'), |
| 2748 | waitForIt('compatNoV2'), |
| 2749 | ]); |
| 2750 | assert.deepStrictEqual(results, [ |
| 2751 | { status: 'fulfilled', value: 'true' }, |
| 2752 | { status: 'fulfilled', value: 'true' }, |
| 2753 | { status: 'fulfilled', value: 'true' }, |
| 2754 | ]); |
| 2755 | }, |
| 2756 | }; |
| 2757 | |
| 2758 | export const stateSizeTest = { |
| 2759 | test() { |
| 2760 | const Uint32Array_orig = Uint32Array; |
| 2761 | assert.throws( |
| 2762 | () => { |
| 2763 | const message = 'Come on, Fhqwhgads.'; |
| 2764 | const buffer = Buffer.from(message); |
| 2765 | globalThis.Uint32Array = function (...args) { |
| 2766 | if (args.length == 1 && arguments[0] == 2) { |
| 2767 | return new Uint32Array_orig(new ArrayBuffer(32), 32); |
| 2768 | } |
| 2769 | return new Uint32Array_orig(...args); |
| 2770 | }; |
| 2771 | |
| 2772 | const zipper = new zlib.Gzip(); |
| 2773 | const zipped = zipper._processChunk(buffer, zlib.constants.FINISH); |
| 2774 | const unzipper = new zlib.Gunzip(); |
| 2775 | unzipper._processChunk(zipped, zlib.constants.FINISH); |
| 2776 | }, |
| 2777 | { |
| 2778 | message: /Invalid write result buffer/, |
| 2779 | } |
| 2780 | ); |
| 2781 | globalThis.Uint32Array = Uint32Array_orig; |
| 2782 | }, |
| 2783 | }; |
| 2784 | |
| 2785 | export const zlibStreamTest = { |
| 2786 | async test() { |
| 2787 | const imgBase64 = |
| 2788 | 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII'; |
| 2789 | |
| 2790 | const buf = Buffer.from(imgBase64, 'base64'); |
| 2791 | const imageStream = Readable.from(Buffer.from(imgBase64, 'base64')); |
| 2792 | const g = zlib.createGzip(); |
| 2793 | |
| 2794 | const { promise, resolve } = Promise.withResolvers(); |
| 2795 | const chunks = []; |
| 2796 | const w = new Writable({ |
| 2797 | construct(callback) { |
| 2798 | // Double queueMicrotask is intentional to reproduce the bug. |
| 2799 | // Do not remove any of them. |
| 2800 | queueMicrotask(() => { |
| 2801 | queueMicrotask(() => { |
| 2802 | callback(null); |
| 2803 | }); |
| 2804 | }); |
| 2805 | }, |
| 2806 | write(chunk, encoding, callback) { |
| 2807 | chunks.push(chunk); |
| 2808 | callback(); |
| 2809 | }, |
| 2810 | }); |
| 2811 | w.on('close', resolve); |
| 2812 | const pp = imageStream.pipe(g); |
| 2813 | pp.pipe(w); |
| 2814 | |
| 2815 | await promise; |
| 2816 | |
| 2817 | assert.strictEqual( |
| 2818 | zlib.gunzipSync(Buffer.concat(chunks)).toString('hex'), |
| 2819 | buf.toString('hex') |
| 2820 | ); |
| 2821 | }, |
| 2822 | }; |
| 2823 | |
| 2824 | // Regression test: after writeSync() returns, z_stream must not retain stale |
| 2825 | // buffer pointers. When handle.params() is called directly (bypassing the |
| 2826 | // TypeScript wrapper's flush-before-params protection), deflateParams() may |
| 2827 | // internally call deflate(Z_BLOCK) to flush pending data. Before the fix, |
| 2828 | // z_stream.next_out still pointed into the kj::Array that backed the output |
| 2829 | // buffer — memory whose BackingStore ref had already been released. If the |
| 2830 | // JS ArrayBuffer was then garbage-collected, that write went to freed memory. |
| 2831 | // The fix clears z_stream buffer pointers after every write, so |
| 2832 | // deflateParams() sees avail_out=0 and returns Z_BUF_ERROR (non-fatal) |
| 2833 | // instead of writing through a dangling pointer. |
| 2834 | export const zlibParamsAfterWriteNoStalePointers = { |
| 2835 | test() { |
| 2836 | // Configuration that leaves data pending in zlib's internal buffers: |
| 2837 | // level 0 (stored blocks) + minimum memory/window + Z_NO_FLUSH. |
| 2838 | const deflate = zlib.createDeflateRaw({ |
| 2839 | level: 0, |
| 2840 | memLevel: 1, |
| 2841 | windowBits: 9, |
| 2842 | }); |
| 2843 | |
| 2844 | const handle = deflate._handle; |
| 2845 | assert.ok(handle, 'native handle should be accessible'); |
| 2846 | |
| 2847 | const input = Buffer.from('A'.repeat(64)); |
| 2848 | const output1 = Buffer.alloc(1024); |
| 2849 | |
| 2850 | // Step 1: writeSync with Z_NO_FLUSH — data is buffered internally. |
| 2851 | handle.writeSync(0, input, 0, input.length, output1, 0, output1.length); |
| 2852 | |
| 2853 | // Capture how many bytes were produced so far. |
| 2854 | const state = deflate._writeState; |
| 2855 | const bytesOut1 = output1.length - state[0]; |
| 2856 | |
| 2857 | // Step 2: params() directly on native handle — no preceding flush. |
| 2858 | // Before the fix this could write to a stale next_out pointer. |
| 2859 | // After the fix this safely returns Z_BUF_ERROR (handled as non-fatal). |
| 2860 | handle.params( |
| 2861 | zlib.constants.Z_DEFAULT_COMPRESSION, |
| 2862 | zlib.constants.Z_DEFAULT_STRATEGY |
| 2863 | ); |
| 2864 | |
| 2865 | // Step 3: finish the stream — must still produce a valid deflate stream. |
| 2866 | const output2 = Buffer.alloc(1024); |
| 2867 | handle.writeSync( |
| 2868 | zlib.constants.Z_FINISH, |
| 2869 | Buffer.alloc(0), |
| 2870 | 0, |
| 2871 | 0, |
| 2872 | output2, |
| 2873 | 0, |
| 2874 | output2.length |
| 2875 | ); |
| 2876 | const bytesOut2 = output2.length - state[0]; |
| 2877 | |
| 2878 | // Step 4: concatenate both output chunks and decompress. |
| 2879 | const compressed = Buffer.concat([ |
| 2880 | output1.slice(0, bytesOut1), |
| 2881 | output2.slice(0, bytesOut2), |
| 2882 | ]); |
| 2883 | const decompressed = zlib.inflateRawSync(compressed, { windowBits: 9 }); |
| 2884 | assert.strictEqual( |
| 2885 | decompressed.toString(), |
| 2886 | input.toString(), |
| 2887 | 'round-trip through write/params/finish must preserve data' |
| 2888 | ); |
| 2889 | }, |
| 2890 | }; |
| 2891 | |
| 2892 | // Regression test for https://github.com/cloudflare/workerd/issues/6286 |
| 2893 | // inflateRawSync throws "Memory limit exceeded" when maxOutputLength is set |
| 2894 | // to exactly the decompressed size. This is an off-by-one error in the |
| 2895 | // GrowableBuffer: after zlib fills the buffer completely (avail_out == 0), |
| 2896 | // the processing loop tries to add another chunk which exceeds maxCapacity. |
| 2897 | export const maxOutputLengthExactSize = { |
| 2898 | test() { |
| 2899 | // Create a known payload and compress it with deflateRaw |
| 2900 | const original = Buffer.from('a]b]c]d]e]f]g]h]i]j]k]l]m]n]o]p]'.repeat(32)); |
| 2901 | const compressed = zlib.deflateRawSync(original); |
| 2902 | const exactSize = original.length; |
| 2903 | |
| 2904 | // This should succeed — maxOutputLength is exactly the output size. |
| 2905 | // Before the fix, this throws RangeError: "Memory limit exceeded" |
| 2906 | const decompressed = zlib.inflateRawSync(compressed, { |
| 2907 | maxOutputLength: exactSize, |
| 2908 | }); |
| 2909 | assert.deepStrictEqual(decompressed, original); |
| 2910 | |
| 2911 | // Sanity check: maxOutputLength + 1 also works |
| 2912 | const decompressed2 = zlib.inflateRawSync(compressed, { |
| 2913 | maxOutputLength: exactSize + 1, |
| 2914 | }); |
| 2915 | assert.deepStrictEqual(decompressed2, original); |
| 2916 | |
| 2917 | // Same bug affects inflateSync |
| 2918 | const compressedZlib = zlib.deflateSync(original); |
| 2919 | const decompressed3 = zlib.inflateSync(compressedZlib, { |
| 2920 | maxOutputLength: exactSize, |
| 2921 | }); |
| 2922 | assert.deepStrictEqual(decompressed3, original); |
| 2923 | |
| 2924 | // And gunzipSync |
| 2925 | const compressedGzip = zlib.gzipSync(original); |
| 2926 | const decompressed4 = zlib.gunzipSync(compressedGzip, { |
| 2927 | maxOutputLength: exactSize, |
| 2928 | }); |
| 2929 | assert.deepStrictEqual(decompressed4, original); |
| 2930 | |
| 2931 | // And brotliDecompressSync |
| 2932 | const compressedBrotli = zlib.brotliCompressSync(original); |
| 2933 | const decompressed5 = zlib.brotliDecompressSync(compressedBrotli, { |
| 2934 | maxOutputLength: exactSize, |
| 2935 | }); |
| 2936 | assert.deepStrictEqual(decompressed5, original); |
| 2937 | |
| 2938 | // Verify that a maxOutputLength that's genuinely too small still throws |
| 2939 | assert.throws( |
| 2940 | () => zlib.inflateRawSync(compressed, { maxOutputLength: exactSize - 1 }), |
| 2941 | RangeError |
| 2942 | ); |
| 2943 | }, |
| 2944 | }; |
| 2945 | |
| 2946 | // Regression test for the exception-safety gap in the clearBuffers() fix. |
| 2947 | // |
| 2948 | // When the writeState buffer passed to initialize() is too small (fewer |
| 2949 | // than 2 uint32 elements), updateWriteResult() throws after deflate() |
| 2950 | // has already run. The original clearBuffers() fix placed explicit calls |
| 2951 | // *after* updateWriteResult(), so the exception unwinds past them — |
| 2952 | // leaving z_stream.next_out pointing into the output buffer's BackingStore. |
| 2953 | // If that buffer is later garbage-collected, a subsequent call to |
| 2954 | // params() -> deflateParams() -> flush_pending() writes to freed memory. |
| 2955 | // |
| 2956 | // The fix uses KJ_DEFER so clearBuffers() runs on every scope exit, |
| 2957 | // including exception unwinding. |
| 2958 | // |
| 2959 | // NOTE: The actual UAF only manifests when GC frees the backing stores |
| 2960 | // (detectable via ASAN + forced gc()). This test exercises the vulnerable |
| 2961 | // code path to guard against regressions and ensure no crash occurs; the |
| 2962 | // memory-safety guarantee is provided by the KJ_DEFER fix itself. |
| 2963 | export const zlibParamsAfterFailedWriteNoStalePointers = { |
| 2964 | test() { |
| 2965 | const streams = []; |
| 2966 | for (let j = 0; j < 10; j++) { |
| 2967 | // Access the native handle directly and re-initialize with a writeState |
| 2968 | // buffer that is intentionally too small (1 uint32 instead of 2). |
| 2969 | // This causes updateWriteResult() to throw after a successful deflate. |
| 2970 | const handle = new zlib.DeflateRaw({ level: 0 })._handle; |
| 2971 | handle.initialize( |
| 2972 | 15, |
| 2973 | 0, |
| 2974 | 8, |
| 2975 | 0, |
| 2976 | Buffer.from(new Uint32Array(1).buffer), |
| 2977 | () => {}, |
| 2978 | undefined |
| 2979 | ); |
| 2980 | |
| 2981 | const payload = Buffer.alloc(64, 0x41); |
| 2982 | let outputBuffer = Buffer.alloc(4096); |
| 2983 | |
| 2984 | // writeSync succeeds in deflate() but throws in updateWriteResult() |
| 2985 | // due to the undersized writeState. Before the KJ_DEFER fix, this |
| 2986 | // left z_stream.next_out pointing into outputBuffer. |
| 2987 | try { |
| 2988 | handle.writeSync( |
| 2989 | 0, |
| 2990 | payload, |
| 2991 | 0, |
| 2992 | payload.length, |
| 2993 | outputBuffer, |
| 2994 | 0, |
| 2995 | outputBuffer.length |
| 2996 | ); |
| 2997 | } catch (_e) { |
| 2998 | // Expected: "Invalid write result buffer" |
| 2999 | } |
| 3000 | streams.push({ handle, outputBuffer }); |
| 3001 | } |
| 3002 | |
| 3003 | // Drop JS references to output buffers. |
| 3004 | for (const s of streams) s.outputBuffer = null; |
| 3005 | |
| 3006 | // params() calls deflateParams() which may internally flush. Before the |
| 3007 | // fix, this could write through a dangling next_out pointer. After the |
| 3008 | // fix, next_out points to a safe dummy byte with avail_out=0, so |
| 3009 | // deflateParams() returns Z_BUF_ERROR (non-fatal). |
| 3010 | for (const s of streams) { |
| 3011 | try { |
| 3012 | s.handle.params( |
| 3013 | zlib.constants.Z_DEFAULT_COMPRESSION, |
| 3014 | zlib.constants.Z_DEFAULT_STRATEGY |
| 3015 | ); |
| 3016 | } catch (_e) { |
| 3017 | // May throw due to stream state; that's fine — the point is no UAF. |
| 3018 | } |
| 3019 | } |
| 3020 | }, |
| 3021 | }; |