From bdfbce924159ece4b32ee7f774a263987e719972 Mon Sep 17 00:00:00 2001 From: Weijia Wang <381152119@qq.com> Date: Sat, 22 Jul 2017 19:23:04 +0800 Subject: [PATCH] http_client, errors: migrate to internal/errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/14423 Refs: https://github.com/nodejs/node/issues/11273 Reviewed-By: Matteo Collina Reviewed-By: Refael Ackermann Reviewed-By: James M Snell Reviewed-By: Tobias Nießen --- doc/api/errors.md | 22 ++++++++++++++ lib/_http_client.js | 27 ++++++++--------- lib/internal/errors.js | 6 ++++ .../test-http-client-check-http-token.js | 7 ++++- ...est-http-client-reject-unexpected-agent.js | 8 +++-- .../test-http-client-unescaped-path.js | 10 +++++-- .../test-http-hostname-typechecking.js | 30 ++++++++++++++----- test/parallel/test-http-invalid-path-chars.js | 9 ++++-- .../test-http-request-invalid-method-error.js | 9 ++++-- test/parallel/test-internal-errors.js | 25 ++++++++++++++++ 10 files changed, 121 insertions(+), 32 deletions(-) diff --git a/doc/api/errors.md b/doc/api/errors.md index 7225e8417f4fb5..fe156524138a54 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -597,6 +597,7 @@ Used when `Console` is instantiated without `stdout` stream or when `stdout` or Used when the native call from `process.cpuUsage` cannot be processed properly. +### ERR_DNS_SET_SERVERS_FAILED Used when `c-ares` failed to set the DNS server. @@ -661,6 +662,11 @@ Used when invalid characters are detected in headers. The `'ERR_INVALID_CURSOR_POS'` is thrown specifically when a cursor on a given stream is attempted to move to a specified row without a specified column. + +### ERR_INVALID_DOMAIN_NAME + +Used when `hostname` can not be parsed from a provided URL. + ### ERR_INVALID_FD @@ -689,7 +695,13 @@ Used when an attempt is made to send an unsupported "handle" over an IPC communication channel to a child process. See [`child.send()`] and [`process.send()`] for more information. + +### ERR_INVALID_HTTP_TOKEN + +Used when `options.method` received an invalid HTTP token. + +### ERR_INVALID_IP_ADDRESS Used when an IP address is not valid. @@ -704,6 +716,11 @@ passed in an options object. Used when an invalid or unknown file encoding is passed. + +### ERR_INVALID_PROTOCOL + +Used when an invalid `options.protocol` is passed. + ### ERR_INVALID_REPL_EVAL_CONFIG @@ -879,6 +896,11 @@ Used to identify a specific kind of internal Node.js error that should not typically be triggered by user code. Instances of this error point to an internal bug within the Node.js binary itself. + +### ERR_UNESCAPED_CHARACTERS + +Used when a string that contains unescaped characters was received. + ### ERR_UNKNOWN_ENCODING diff --git a/lib/_http_client.js b/lib/_http_client.js index 29ea688ecb1bf0..f141fd785c9d36 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -37,6 +37,7 @@ const Buffer = require('buffer').Buffer; const { urlToOptions, searchParamsSymbol } = require('internal/url'); const outHeadersKey = require('internal/http').outHeadersKey; const nextTick = require('internal/process/next_tick').nextTick; +const errors = require('internal/errors'); // The actual list of disallowed characters in regexp form is more like: // /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/ @@ -68,8 +69,8 @@ function isInvalidPath(s) { function validateHost(host, name) { if (host != null && typeof host !== 'string') { - throw new TypeError( - `"options.${name}" must either be a string, undefined or null`); + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', `options.${name}`, + ['string', 'undefined', 'null'], host); } return host; } @@ -80,7 +81,7 @@ function ClientRequest(options, cb) { if (typeof options === 'string') { options = url.parse(options); if (!options.hostname) { - throw new Error('Unable to determine the domain name'); + throw new errors.Error('ERR_INVALID_DOMAIN_NAME'); } } else if (options && options[searchParamsSymbol] && options[searchParamsSymbol][searchParamsSymbol]) { @@ -101,9 +102,8 @@ function ClientRequest(options, cb) { // Explicitly pass through this statement as agent will not be used // when createConnection is provided. } else if (typeof agent.addRequest !== 'function') { - throw new TypeError( - 'Agent option must be an Agent-like object, undefined, or false.' - ); + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'Agent option', + ['Agent-like object', 'undefined', 'false']); } this.agent = agent; @@ -122,12 +122,11 @@ function ClientRequest(options, cb) { invalidPath = /[\u0000-\u0020]/.test(path); } if (invalidPath) - throw new TypeError('Request path contains unescaped characters'); + throw new errors.TypeError('ERR_UNESCAPED_CHARACTERS', 'Request path'); } if (protocol !== expectedProtocol) { - throw new Error('Protocol "' + protocol + '" not supported. ' + - 'Expected "' + expectedProtocol + '"'); + throw new errors.Error('ERR_INVALID_PROTOCOL', protocol, expectedProtocol); } var defaultPort = options.defaultPort || @@ -145,12 +144,13 @@ function ClientRequest(options, cb) { var method = options.method; var methodIsString = (typeof method === 'string'); if (method != null && !methodIsString) { - throw new TypeError('Method must be a string'); + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'method', + 'string', method); } if (methodIsString && method) { if (!common._checkIsHttpToken(method)) { - throw new TypeError('Method must be a valid HTTP token'); + throw new errors.TypeError('ERR_INVALID_HTTP_TOKEN', 'Method'); } method = this.method = method.toUpperCase(); } else { @@ -211,8 +211,7 @@ function ClientRequest(options, cb) { options.headers); } else if (this.getHeader('expect')) { if (this._header) { - throw new Error('Can\'t render headers after they are sent to the ' + - 'client'); + throw new errors.Error('ERR_HTTP_HEADERS_SENT'); } this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n', @@ -303,7 +302,7 @@ ClientRequest.prototype._finish = function _finish() { ClientRequest.prototype._implicitHeader = function _implicitHeader() { if (this._header) { - throw new Error('Can\'t render headers after they are sent to the client'); + throw new errors.Error('ERR_HTTP_HEADERS_SENT'); } this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n', this[outHeadersKey]); diff --git a/lib/internal/errors.js b/lib/internal/errors.js index df26230a6fc8ee..9989528f76ea01 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -130,11 +130,13 @@ E('ERR_INVALID_CALLBACK', 'Callback must be a function'); E('ERR_INVALID_CHAR', 'Invalid character in %s'); E('ERR_INVALID_CURSOR_POS', 'Cannot set cursor row without setting its column'); +E('ERR_INVALID_DOMAIN_NAME', 'Unable to determine the domain name'); E('ERR_INVALID_FD', '"fd" must be a positive integer: %s'); E('ERR_INVALID_FILE_URL_HOST', 'File URL host must be "localhost" or empty on %s'); E('ERR_INVALID_FILE_URL_PATH', 'File URL path %s'); E('ERR_INVALID_HANDLE_TYPE', 'This handle type cannot be sent'); +E('ERR_INVALID_HTTP_TOKEN', (name) => `${name} must be a valid HTTP token`); E('ERR_INVALID_IP_ADDRESS', 'Invalid IP address: %s'); E('ERR_INVALID_OPT_VALUE', (name, value) => { @@ -142,6 +144,8 @@ E('ERR_INVALID_OPT_VALUE', }); E('ERR_INVALID_OPT_VALUE_ENCODING', (value) => `The value "${String(value)}" is invalid for option "encoding"`); +E('ERR_INVALID_PROTOCOL', (protocol, expectedProtocol) => + `Protocol "${protocol}" not supported. Expected "${expectedProtocol}"`); E('ERR_INVALID_REPL_EVAL_CONFIG', 'Cannot specify both "breakEvalOnSigint" and "eval" for REPL'); E('ERR_INVALID_SYNC_FORK_INPUT', @@ -185,6 +189,8 @@ E('ERR_TRANSFORM_ALREADY_TRANSFORMING', 'Calling transform done when still transforming'); E('ERR_TRANSFORM_WITH_LENGTH_0', 'Calling transform done when writableState.length != 0'); +E('ERR_UNESCAPED_CHARACTERS', + (name) => `${name} contains unescaped characters`); E('ERR_UNKNOWN_ENCODING', 'Unknown encoding: %s'); E('ERR_UNKNOWN_SIGNAL', 'Unknown signal: %s'); E('ERR_UNKNOWN_STDIN_TYPE', 'Unknown stdin file type'); diff --git a/test/parallel/test-http-client-check-http-token.js b/test/parallel/test-http-client-check-http-token.js index 4fa3a44802a5f0..7a0c894504b1b9 100644 --- a/test/parallel/test-http-client-check-http-token.js +++ b/test/parallel/test-http-client-check-http-token.js @@ -20,7 +20,12 @@ server.listen(0, common.mustCall(() => { expectedFails.forEach((method) => { assert.throws(() => { http.request({ method, path: '/' }, common.mustNotCall()); - }, /^TypeError: Method must be a string$/); + }, common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "method" argument must be of type string. ' + + `Received type ${typeof method}` + })); }); expectedSuccesses.forEach((method) => { diff --git a/test/parallel/test-http-client-reject-unexpected-agent.js b/test/parallel/test-http-client-reject-unexpected-agent.js index f1798b483f44fa..23439baa32f6f9 100644 --- a/test/parallel/test-http-client-reject-unexpected-agent.js +++ b/test/parallel/test-http-client-reject-unexpected-agent.js @@ -49,8 +49,12 @@ server.listen(0, baseOptions.host, common.mustCall(function() { failingAgentOptions.forEach((agent) => { assert.throws( () => createRequest(agent), - /^TypeError: Agent option must be an Agent-like object/, - `Expected typeof agent: ${typeof agent} to be rejected` + common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "Agent option" argument must be one of type ' + + 'Agent-like object, undefined, or false' + }) ); }); diff --git a/test/parallel/test-http-client-unescaped-path.js b/test/parallel/test-http-client-unescaped-path.js index 11faaec41a6035..f3c680919df001 100644 --- a/test/parallel/test-http-client-unescaped-path.js +++ b/test/parallel/test-http-client-unescaped-path.js @@ -24,8 +24,14 @@ const common = require('../common'); const assert = require('assert'); const http = require('http'); -const errMessage = /contains unescaped characters/; for (let i = 0; i <= 32; i += 1) { const path = `bad${String.fromCharCode(i)}path`; - assert.throws(() => http.get({ path }, common.mustNotCall()), errMessage); + assert.throws( + () => http.get({ path }, common.mustNotCall()), + common.expectsError({ + code: 'ERR_UNESCAPED_CHARACTERS', + type: TypeError, + message: 'Request path contains unescaped characters' + }) + ); } diff --git a/test/parallel/test-http-hostname-typechecking.js b/test/parallel/test-http-hostname-typechecking.js index 91d4d99b9a524e..dbb98b7ae11c5e 100644 --- a/test/parallel/test-http-hostname-typechecking.js +++ b/test/parallel/test-http-hostname-typechecking.js @@ -1,6 +1,6 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const http = require('http'); @@ -8,14 +8,28 @@ const http = require('http'); // when passed as the value of either options.hostname or options.host const vals = [{}, [], NaN, Infinity, -Infinity, true, false, 1, 0, new Date()]; -const errHostname = - /^TypeError: "options\.hostname" must either be a string, undefined or null$/; -const errHost = - /^TypeError: "options\.host" must either be a string, undefined or null$/; - vals.forEach((v) => { - assert.throws(() => http.request({ hostname: v }), errHostname); - assert.throws(() => http.request({ host: v }), errHost); + assert.throws( + () => http.request({ hostname: v }), + common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "options.hostname" property must be one of ' + + 'type string, undefined, or null. ' + + `Received type ${typeof v}` + }) + ); + + assert.throws( + () => http.request({ host: v }), + common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "options.host" property must be one of ' + + 'type string, undefined, or null. ' + + `Received type ${typeof v}` + }) + ); }); // These values are OK and should not throw synchronously. diff --git a/test/parallel/test-http-invalid-path-chars.js b/test/parallel/test-http-invalid-path-chars.js index 6aedf430a156cf..c1d0baa62c3e9b 100644 --- a/test/parallel/test-http-invalid-path-chars.js +++ b/test/parallel/test-http-invalid-path-chars.js @@ -1,9 +1,14 @@ 'use strict'; -require('../common'); + +const common = require('../common'); const assert = require('assert'); const http = require('http'); -const expectedError = /^TypeError: Request path contains unescaped characters$/; +const expectedError = common.expectsError({ + code: 'ERR_UNESCAPED_CHARACTERS', + type: TypeError, + message: 'Request path contains unescaped characters' +}, 1320); const theExperimentallyDeterminedNumber = 39; function fail(path) { diff --git a/test/parallel/test-http-request-invalid-method-error.js b/test/parallel/test-http-request-invalid-method-error.js index d5dffdd2212da9..457e90fd48843c 100644 --- a/test/parallel/test-http-request-invalid-method-error.js +++ b/test/parallel/test-http-request-invalid-method-error.js @@ -4,7 +4,10 @@ const assert = require('assert'); const http = require('http'); assert.throws( - () => { http.request({ method: '\0' }); }, - common.expectsError({ type: TypeError, - message: 'Method must be a valid HTTP token' }) + () => http.request({ method: '\0' }), + common.expectsError({ + code: 'ERR_INVALID_HTTP_TOKEN', + type: TypeError, + message: 'Method must be a valid HTTP token' + }) ); diff --git a/test/parallel/test-internal-errors.js b/test/parallel/test-internal-errors.js index 1166b0bed2a09a..8df0780e04055c 100644 --- a/test/parallel/test-internal-errors.js +++ b/test/parallel/test-internal-errors.js @@ -235,3 +235,28 @@ assert.throws( assert.strictEqual( errors.message('ERR_TLS_CERT_ALTNAME_INVALID', ['altname']), 'Hostname/IP does not match certificate\'s altnames: altname'); + +assert.strictEqual( + errors.message('ERR_INVALID_PROTOCOL', ['bad protocol', 'http']), + 'Protocol "bad protocol" not supported. Expected "http"' +); + +assert.strictEqual( + errors.message('ERR_HTTP_HEADERS_SENT'), + 'Cannot render headers after they are sent to the client' +); + +assert.strictEqual( + errors.message('ERR_INVALID_DOMAIN_NAME'), + 'Unable to determine the domain name' +); + +assert.strictEqual( + errors.message('ERR_INVALID_HTTP_TOKEN', ['Method']), + 'Method must be a valid HTTP token' +); + +assert.strictEqual( + errors.message('ERR_UNESCAPED_CHARACTERS', ['Request path']), + 'Request path contains unescaped characters' +);