From 6b2143b04ea194b3ac744267b255f2c1a32c025e Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 25 Sep 2017 16:48:57 -0700 Subject: [PATCH] zlib: migrate to internal/errors PR-URL: https://github.com/nodejs/node/pull/15618 Reviewed-By: Anna Henningsen Reviewed-By: Ruben Bridgewater Reviewed-By: Luigi Pinca Reviewed-By: Minwoo Jung Reviewed-By: Michael Dawson --- doc/api/errors.md | 18 +++ lib/internal/errors.js | 11 ++ lib/zlib.js | 54 ++++---- test/parallel/test-internal-errors.js | 17 +++ .../test-zlib-deflate-constructors.js | 119 ++++++++++++------ test/parallel/test-zlib-failed-init.js | 36 ++++-- test/parallel/test-zlib-flush-flags.js | 42 +++++-- .../test-zlib-not-string-or-buffer.js | 25 ++-- test/parallel/test-zlib-write-after-close.js | 10 +- 9 files changed, 231 insertions(+), 101 deletions(-) diff --git a/doc/api/errors.md b/doc/api/errors.md index dc530c7589..f217103e36 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -609,6 +609,12 @@ also able to define their own types when using the public embedder API. Used when attempting to perform an operation outside the bounds of a `Buffer`. + +### ERR_BUFFER_TOO_LARGE + +Used when an attempt has been made to create a `Buffer` larger than the +maximum allowed size. + ### ERR_CHILD_CLOSED_BEFORE_REPLY @@ -879,6 +885,12 @@ Used when a given index is out of the accepted range (e.g. negative offsets). Used generically to identify that an argument of the wrong type has been passed to a Node.js API. + +### ERR_INVALID_ARG_VALUE + +Used generically to identify that an invalid or unsupported value has been +passed for a given argument. + ### ERR_INVALID_ARRAY_LENGTH @@ -1277,6 +1289,12 @@ entry types were found. Used when a given value is out of the accepted range. + +### ERR_ZLIB_BINDING_CLOSED + +Used when an attempt is made to use a `zlib` object after it has already been +closed. + [`ERR_INVALID_ARG_TYPE`]: #ERR_INVALID_ARG_TYPE [`subprocess.kill()`]: child_process.html#child_process_subprocess_kill_signal [`subprocess.send()`]: child_process.html#child_process_subprocess_send_message_sendhandle_options_callback diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 175144078a..48ef2dcc3d 100755 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -11,6 +11,10 @@ const kCode = Symbol('code'); const messages = new Map(); +const { + kMaxLength +} = process.binding('buffer'); + // Lazily loaded var util = null; @@ -121,6 +125,8 @@ E('ERR_ASSERTION', '%s'); E('ERR_ASYNC_CALLBACK', (name) => `${name} must be a function`); E('ERR_ASYNC_TYPE', (s) => `Invalid name for async "type": ${s}`); E('ERR_BUFFER_OUT_OF_BOUNDS', bufferOutOfBounds); +E('ERR_BUFFER_TOO_LARGE', + `Cannot create a Buffer larger than 0x${kMaxLength.toString(16)} bytes`); E('ERR_CHILD_CLOSED_BEFORE_REPLY', 'Child closed before reply received'); E('ERR_CONSOLE_WRITABLE_STREAM', 'Console expects a writable stream instance for %s'); @@ -206,6 +212,10 @@ E('ERR_HTTP_TRAILER_INVALID', 'Trailers are invalid with this transfer encoding'); E('ERR_INDEX_OUT_OF_RANGE', 'Index out of range'); E('ERR_INVALID_ARG_TYPE', invalidArgType); +E('ERR_INVALID_ARG_VALUE', + (name, value) => { + return `The value "${String(value)}" is invalid for argument "${name}"`; + }); E('ERR_INVALID_ARRAY_LENGTH', (name, len, actual) => { internalAssert(typeof actual === 'number', 'actual must be a number'); @@ -303,6 +313,7 @@ E('ERR_VALID_PERFORMANCE_ENTRY_TYPE', E('ERR_VALUE_OUT_OF_RANGE', (start, end, value) => { return `The value of "${start}" must be ${end}. Received "${value}"`; }); +E('ERR_ZLIB_BINDING_CLOSED', 'zlib binding closed'); function invalidArgType(name, expected, actual) { internalAssert(name, 'name is required'); diff --git a/lib/zlib.js b/lib/zlib.js index 49cf8f0315..059b9aac91 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -21,15 +21,16 @@ 'use strict'; -const Buffer = require('buffer').Buffer; +const errors = require('internal/errors'); const Transform = require('_stream_transform'); const { _extend } = require('util'); const { isArrayBufferView } = require('internal/util/types'); const binding = process.binding('zlib'); const assert = require('assert').ok; -const kMaxLength = require('buffer').kMaxLength; -const kRangeErrorMessage = 'Cannot create final Buffer. It would be larger ' + - `than 0x${kMaxLength.toString(16)} bytes`; +const { + Buffer, + kMaxLength +} = require('buffer'); const constants = process.binding('constants').zlib; const { @@ -93,7 +94,7 @@ function zlibBufferOnEnd() { var buf; var err; if (this.nread >= kMaxLength) { - err = new RangeError(kRangeErrorMessage); + err = new errors.RangeError('ERR_BUFFER_TOO_LARGE'); } else { var bufs = this.buffers; buf = (bufs.length === 1 ? bufs[0] : Buffer.concat(bufs, this.nread)); @@ -111,8 +112,9 @@ function zlibBufferSync(engine, buffer) { if (typeof buffer === 'string') { buffer = Buffer.from(buffer); } else if (!isArrayBufferView(buffer)) { - throw new TypeError('"buffer" argument must be a string, Buffer, ' + - 'TypedArray, or DataView'); + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', + 'buffer', + ['string', 'Buffer', 'TypedArray', 'DataView']); } buffer = processChunkSync(engine, buffer, engine._finishFlushFlag); if (engine._info) @@ -128,7 +130,7 @@ function zlibOnError(message, errno) { _close(self); self._hadError = true; - var error = new Error(message); + const error = new Error(message); error.errno = errno; error.code = codes[errno]; self.emit('error', error); @@ -163,7 +165,9 @@ function Zlib(opts, mode) { chunkSize = opts.chunkSize; if (chunkSize !== undefined && chunkSize === chunkSize) { if (chunkSize < Z_MIN_CHUNK || !Number.isFinite(chunkSize)) - throw new RangeError('Invalid chunk size: ' + chunkSize); + throw new errors.RangeError('ERR_INVALID_OPT_VALUE', + 'chunkSize', + chunkSize); } else { chunkSize = Z_DEFAULT_CHUNK; } @@ -171,7 +175,7 @@ function Zlib(opts, mode) { flush = opts.flush; if (flush !== undefined && flush === flush) { if (flush < Z_NO_FLUSH || flush > Z_BLOCK || !Number.isFinite(flush)) - throw new RangeError('Invalid flush flag: ' + flush); + throw new errors.RangeError('ERR_INVALID_OPT_VALUE', 'flush', flush); } else { flush = Z_NO_FLUSH; } @@ -180,7 +184,9 @@ function Zlib(opts, mode) { if (finishFlush !== undefined && finishFlush === finishFlush) { if (finishFlush < Z_NO_FLUSH || finishFlush > Z_BLOCK || !Number.isFinite(finishFlush)) { - throw new RangeError('Invalid flush flag: ' + finishFlush); + throw new errors.RangeError('ERR_INVALID_OPT_VALUE', + 'finishFlush', + finishFlush); } } else { finishFlush = Z_FINISH; @@ -190,7 +196,9 @@ function Zlib(opts, mode) { if (windowBits !== undefined && windowBits === windowBits) { if (windowBits < Z_MIN_WINDOWBITS || windowBits > Z_MAX_WINDOWBITS || !Number.isFinite(windowBits)) { - throw new RangeError('Invalid windowBits: ' + windowBits); + throw new errors.RangeError('ERR_INVALID_OPT_VALUE', + 'windowBits', + windowBits); } } else { windowBits = Z_DEFAULT_WINDOWBITS; @@ -200,7 +208,8 @@ function Zlib(opts, mode) { if (level !== undefined && level === level) { if (level < Z_MIN_LEVEL || level > Z_MAX_LEVEL || !Number.isFinite(level)) { - throw new RangeError('Invalid compression level: ' + level); + throw new errors.RangeError('ERR_INVALID_OPT_VALUE', + 'level', level); } } else { level = Z_DEFAULT_COMPRESSION; @@ -210,7 +219,8 @@ function Zlib(opts, mode) { if (memLevel !== undefined && memLevel === memLevel) { if (memLevel < Z_MIN_MEMLEVEL || memLevel > Z_MAX_MEMLEVEL || !Number.isFinite(memLevel)) { - throw new RangeError('Invalid memLevel: ' + memLevel); + throw new errors.RangeError('ERR_INVALID_OPT_VALUE', + 'memLevel', memLevel); } } else { memLevel = Z_DEFAULT_MEMLEVEL; @@ -220,7 +230,8 @@ function Zlib(opts, mode) { if (strategy !== undefined && strategy === strategy) { if (strategy < Z_DEFAULT_STRATEGY || strategy > Z_FIXED || !Number.isFinite(strategy)) { - throw new TypeError('Invalid strategy: ' + strategy); + throw new errors.TypeError('ERR_INVALID_OPT_VALUE', + 'strategy', strategy); } } else { strategy = Z_DEFAULT_STRATEGY; @@ -228,8 +239,9 @@ function Zlib(opts, mode) { dictionary = opts.dictionary; if (dictionary !== undefined && !isArrayBufferView(dictionary)) { - throw new TypeError( - 'Invalid dictionary: it should be a Buffer, TypedArray, or DataView'); + throw new errors.TypeError('ERR_INVALID_OPT_VALUE', + 'dictionary', + dictionary); } if (opts.encoding || opts.objectMode || opts.writableObjectMode) { @@ -273,12 +285,12 @@ Object.defineProperty(Zlib.prototype, '_closed', { Zlib.prototype.params = function params(level, strategy, callback) { if (level < Z_MIN_LEVEL || level > Z_MAX_LEVEL) - throw new RangeError('Invalid compression level: ' + level); + throw new errors.RangeError('ERR_INVALID_ARG_VALUE', 'level', level); if (strategy !== undefined && (strategy < Z_DEFAULT_STRATEGY || strategy > Z_FIXED || !Number.isFinite(strategy))) { - throw new TypeError('Invalid strategy: ' + strategy); + throw new errors.TypeError('ERR_INVALID_ARG_VALUE', 'strategy', strategy); } if (this._level !== level || this._strategy !== strategy) { @@ -455,7 +467,7 @@ function processChunkSync(self, chunk, flushFlag) { if (nread >= kMaxLength) { _close(self); - throw new RangeError(kRangeErrorMessage); + throw new errors.RangeError('ERR_BUFFER_TOO_LARGE'); } _close(self); @@ -466,7 +478,7 @@ function processChunkSync(self, chunk, flushFlag) { function processChunk(self, chunk, flushFlag, cb) { var handle = self._handle; if (!handle) - return cb(new Error('zlib binding closed')); + return cb(new errors.Error('ERR_ZLIB_BINDING_CLOSED')); handle.buffer = chunk; handle.cb = cb; diff --git a/test/parallel/test-internal-errors.js b/test/parallel/test-internal-errors.js index 1c16823e7a..410c5aa974 100644 --- a/test/parallel/test-internal-errors.js +++ b/test/parallel/test-internal-errors.js @@ -284,3 +284,20 @@ assert.strictEqual( assert.strictEqual( errors.message('ERR_INVALID_ASYNC_ID', ['asyncId', undefined]), 'Invalid asyncId value: undefined'); + +{ + const { kMaxLength } = process.binding('buffer'); + const error = new errors.Error('ERR_BUFFER_TOO_LARGE'); + assert.strictEqual( + error.message, + `Cannot create a Buffer larger than 0x${kMaxLength.toString(16)} bytes` + ); +} + +{ + const error = new errors.Error('ERR_INVALID_ARG_VALUE', 'foo', 'bar'); + assert.strictEqual( + error.message, + 'The value "bar" is invalid for argument "foo"' + ); +} diff --git a/test/parallel/test-zlib-deflate-constructors.js b/test/parallel/test-zlib-deflate-constructors.js index e4b39002fb..090fbb8d07 100644 --- a/test/parallel/test-zlib-deflate-constructors.js +++ b/test/parallel/test-zlib-deflate-constructors.js @@ -1,6 +1,6 @@ 'use strict'; -require('../common'); +const common = require('../common'); const zlib = require('zlib'); const assert = require('assert'); @@ -13,56 +13,83 @@ assert.ok(zlib.DeflateRaw() instanceof zlib.DeflateRaw); assert.ok(new zlib.DeflateRaw() instanceof zlib.DeflateRaw); // Throws if `opts.chunkSize` is invalid -assert.throws( - () => { new zlib.Deflate({ chunkSize: -Infinity }); }, - /^RangeError: Invalid chunk size: -Infinity$/ +common.expectsError( + () => new zlib.Deflate({ chunkSize: -Infinity }), + { + code: 'ERR_INVALID_OPT_VALUE', + type: RangeError + } ); // Confirm that maximum chunk size cannot be exceeded because it is `Infinity`. assert.strictEqual(zlib.constants.Z_MAX_CHUNK, Infinity); // Throws if `opts.windowBits` is invalid -assert.throws( - () => { new zlib.Deflate({ windowBits: -Infinity }); }, - /^RangeError: Invalid windowBits: -Infinity$/ +common.expectsError( + () => new zlib.Deflate({ windowBits: -Infinity }), + { + code: 'ERR_INVALID_OPT_VALUE', + type: RangeError + } ); -assert.throws( - () => { new zlib.Deflate({ windowBits: Infinity }); }, - /^RangeError: Invalid windowBits: Infinity$/ +common.expectsError( + () => new zlib.Deflate({ windowBits: Infinity }), + { + code: 'ERR_INVALID_OPT_VALUE', + type: RangeError + } ); // Throws if `opts.level` is invalid -assert.throws( - () => { new zlib.Deflate({ level: -Infinity }); }, - /^RangeError: Invalid compression level: -Infinity$/ +common.expectsError( + () => new zlib.Deflate({ level: -Infinity }), + { + code: 'ERR_INVALID_OPT_VALUE', + type: RangeError + } ); -assert.throws( - () => { new zlib.Deflate({ level: Infinity }); }, - /^RangeError: Invalid compression level: Infinity$/ +common.expectsError( + () => new zlib.Deflate({ level: Infinity }), + { + code: 'ERR_INVALID_OPT_VALUE', + type: RangeError + } ); // Throws a RangeError if `level` invalid in `Deflate.prototype.params()` -assert.throws( - () => { new zlib.Deflate().params(-Infinity); }, - /^RangeError: Invalid compression level: -Infinity$/ +common.expectsError( + () => new zlib.Deflate().params(-Infinity), + { + code: 'ERR_INVALID_ARG_VALUE', + type: RangeError + } ); -assert.throws( - () => { new zlib.Deflate().params(Infinity); }, - /^RangeError: Invalid compression level: Infinity$/ +common.expectsError( + () => new zlib.Deflate().params(Infinity), + { + code: 'ERR_INVALID_ARG_VALUE', + type: RangeError + } ); // Throws if `opts.memLevel` is invalid -assert.throws( - () => { new zlib.Deflate({ memLevel: -Infinity }); }, - /^RangeError: Invalid memLevel: -Infinity$/ +common.expectsError( + () => new zlib.Deflate({ memLevel: -Infinity }), + { + code: 'ERR_INVALID_OPT_VALUE', + type: RangeError + } ); -assert.throws( - () => { new zlib.Deflate({ memLevel: Infinity }); }, - /^RangeError: Invalid memLevel: Infinity$/ +common.expectsError( + () => new zlib.Deflate({ memLevel: Infinity }), + { + code: 'ERR_INVALID_OPT_VALUE', + type: RangeError + } ); // Does not throw if opts.strategy is valid @@ -87,25 +114,37 @@ assert.doesNotThrow( ); // Throws if opt.strategy is the wrong type. -assert.throws( - () => { new zlib.Deflate({ strategy: String(zlib.constants.Z_RLE) }); }, - /^TypeError: Invalid strategy: 3$/ +common.expectsError( + () => new zlib.Deflate({ strategy: String(zlib.constants.Z_RLE) }), + { + code: 'ERR_INVALID_OPT_VALUE', + type: TypeError + } ); // Throws if opts.strategy is invalid -assert.throws( - () => { new zlib.Deflate({ strategy: 'this is a bogus strategy' }); }, - /^TypeError: Invalid strategy: this is a bogus strategy$/ +common.expectsError( + () => new zlib.Deflate({ strategy: 'this is a bogus strategy' }), + { + code: 'ERR_INVALID_OPT_VALUE', + type: TypeError + } ); // Throws TypeError if `strategy` is invalid in `Deflate.prototype.params()` -assert.throws( - () => { new zlib.Deflate().params(0, 'I am an invalid strategy'); }, - /^TypeError: Invalid strategy: I am an invalid strategy$/ +common.expectsError( + () => new zlib.Deflate().params(0, 'I am an invalid strategy'), + { + code: 'ERR_INVALID_ARG_VALUE', + type: TypeError + } ); // Throws if opts.dictionary is not a Buffer -assert.throws( - () => { new zlib.Deflate({ dictionary: 'not a buffer' }); }, - /^TypeError: Invalid dictionary: it should be a Buffer, TypedArray, or DataView$/ +common.expectsError( + () => new zlib.Deflate({ dictionary: 'not a buffer' }), + { + code: 'ERR_INVALID_OPT_VALUE', + type: TypeError + } ); diff --git a/test/parallel/test-zlib-failed-init.js b/test/parallel/test-zlib-failed-init.js index 4f224ecd61..8597543429 100644 --- a/test/parallel/test-zlib-failed-init.js +++ b/test/parallel/test-zlib-failed-init.js @@ -1,6 +1,6 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const zlib = require('zlib'); @@ -18,17 +18,29 @@ if (!/^1\.2\.[0-8]$/.test(process.versions.zlib)) { // Regression tests for bugs in the validation logic. -assert.throws(() => { - zlib.createGzip({ chunkSize: 0 }); -}, /^RangeError: Invalid chunk size: 0$/); - -assert.throws(() => { - zlib.createGzip({ windowBits: 0 }); -}, /^RangeError: Invalid windowBits: 0$/); - -assert.throws(() => { - zlib.createGzip({ memLevel: 0 }); -}, /^RangeError: Invalid memLevel: 0$/); +common.expectsError( + () => zlib.createGzip({ chunkSize: 0 }), + { + code: 'ERR_INVALID_OPT_VALUE', + type: RangeError + } +); + +common.expectsError( + () => zlib.createGzip({ windowBits: 0 }), + { + code: 'ERR_INVALID_OPT_VALUE', + type: RangeError + } +); + +common.expectsError( + () => zlib.createGzip({ memLevel: 0 }), + { + code: 'ERR_INVALID_OPT_VALUE', + type: RangeError + } +); { const stream = zlib.createGzip({ level: NaN }); diff --git a/test/parallel/test-zlib-flush-flags.js b/test/parallel/test-zlib-flush-flags.js index d5e79be53b..25cfad70e3 100644 --- a/test/parallel/test-zlib-flush-flags.js +++ b/test/parallel/test-zlib-flush-flags.js @@ -1,5 +1,5 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const zlib = require('zlib'); @@ -7,22 +7,38 @@ assert.doesNotThrow(() => { zlib.createGzip({ flush: zlib.constants.Z_SYNC_FLUSH }); }); -assert.throws(() => { - zlib.createGzip({ flush: 'foobar' }); -}, /^RangeError: Invalid flush flag: foobar$/); +common.expectsError( + () => zlib.createGzip({ flush: 'foobar' }), + { + code: 'ERR_INVALID_OPT_VALUE', + type: RangeError + } +); -assert.throws(() => { - zlib.createGzip({ flush: 10000 }); -}, /^RangeError: Invalid flush flag: 10000$/); +common.expectsError( + () => zlib.createGzip({ flush: 10000 }), + { + code: 'ERR_INVALID_OPT_VALUE', + type: RangeError + } +); assert.doesNotThrow(() => { zlib.createGzip({ finishFlush: zlib.constants.Z_SYNC_FLUSH }); }); -assert.throws(() => { - zlib.createGzip({ finishFlush: 'foobar' }); -}, /^RangeError: Invalid flush flag: foobar$/); +common.expectsError( + () => zlib.createGzip({ finishFlush: 'foobar' }), + { + code: 'ERR_INVALID_OPT_VALUE', + type: RangeError + } +); -assert.throws(() => { - zlib.createGzip({ finishFlush: 10000 }); -}, /^RangeError: Invalid flush flag: 10000$/); +common.expectsError( + () => zlib.createGzip({ finishFlush: 10000 }), + { + code: 'ERR_INVALID_OPT_VALUE', + type: RangeError + } +); diff --git a/test/parallel/test-zlib-not-string-or-buffer.js b/test/parallel/test-zlib-not-string-or-buffer.js index d03d69fea1..94db59d77f 100644 --- a/test/parallel/test-zlib-not-string-or-buffer.js +++ b/test/parallel/test-zlib-not-string-or-buffer.js @@ -3,18 +3,17 @@ // Check the error condition testing for passing something other than a string // or buffer. -require('../common'); -const assert = require('assert'); +const common = require('../common'); const zlib = require('zlib'); -const expected = - /^TypeError: "buffer" argument must be a string, Buffer, TypedArray, or DataView$/; - -assert.throws(() => { zlib.deflateSync(undefined); }, expected); -assert.throws(() => { zlib.deflateSync(null); }, expected); -assert.throws(() => { zlib.deflateSync(true); }, expected); -assert.throws(() => { zlib.deflateSync(false); }, expected); -assert.throws(() => { zlib.deflateSync(0); }, expected); -assert.throws(() => { zlib.deflateSync(1); }, expected); -assert.throws(() => { zlib.deflateSync([1, 2, 3]); }, expected); -assert.throws(() => { zlib.deflateSync({ foo: 'bar' }); }, expected); +[undefined, null, true, false, 0, 1, [1, 2, 3], { foo: 'bar' }].forEach((i) => { + common.expectsError( + () => zlib.deflateSync(i), + { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "buffer" argument must be one of type string, Buffer, ' + + 'TypedArray, or DataView' + } + ); +}); diff --git a/test/parallel/test-zlib-write-after-close.js b/test/parallel/test-zlib-write-after-close.js index aabe3cfd84..88d6643da8 100644 --- a/test/parallel/test-zlib-write-after-close.js +++ b/test/parallel/test-zlib-write-after-close.js @@ -21,11 +21,17 @@ 'use strict'; const common = require('../common'); -const assert = require('assert'); const zlib = require('zlib'); zlib.gzip('hello', common.mustCall(function(err, out) { const unzip = zlib.createGunzip(); unzip.close(common.mustCall()); - assert.throws(() => unzip.write(out), /^Error: zlib binding closed$/); + common.expectsError( + () => unzip.write(out), + { + code: 'ERR_ZLIB_BINDING_CLOSED', + type: Error, + message: 'zlib binding closed' + } + ); }));