Skip to content

Commit bdfbce9

Browse files
starkwangtniessen
authored andcommitted
http_client, errors: migrate to internal/errors
PR-URL: #14423 Refs: #11273 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Tobias Nießen <[email protected]>
1 parent 8db3997 commit bdfbce9

10 files changed

+121
-32
lines changed

doc/api/errors.md

+22
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,7 @@ Used when `Console` is instantiated without `stdout` stream or when `stdout` or
597597
Used when the native call from `process.cpuUsage` cannot be processed properly.
598598

599599
<a id="ERR_DNS_SET_SERVERS_FAILED"></a>
600+
### ERR_DNS_SET_SERVERS_FAILED
600601

601602
Used when `c-ares` failed to set the DNS server.
602603

@@ -661,6 +662,11 @@ Used when invalid characters are detected in headers.
661662
The `'ERR_INVALID_CURSOR_POS'` is thrown specifically when a cursor on a given
662663
stream is attempted to move to a specified row without a specified column.
663664

665+
<a id="ERR_INVALID_DOMAIN_NAME"></a>
666+
### ERR_INVALID_DOMAIN_NAME
667+
668+
Used when `hostname` can not be parsed from a provided URL.
669+
664670
<a id="ERR_INVALID_FD"></a>
665671
### ERR_INVALID_FD
666672

@@ -689,7 +695,13 @@ Used when an attempt is made to send an unsupported "handle" over an IPC
689695
communication channel to a child process. See [`child.send()`] and
690696
[`process.send()`] for more information.
691697

698+
<a id="ERR_INVALID_HTTP_TOKEN"></a>
699+
### ERR_INVALID_HTTP_TOKEN
700+
701+
Used when `options.method` received an invalid HTTP token.
702+
692703
<a id="ERR_INVALID_IP_ADDRESS"></a>
704+
### ERR_INVALID_IP_ADDRESS
693705

694706
Used when an IP address is not valid.
695707

@@ -704,6 +716,11 @@ passed in an options object.
704716

705717
Used when an invalid or unknown file encoding is passed.
706718

719+
<a id="ERR_INVALID_PROTOCOL"></a>
720+
### ERR_INVALID_PROTOCOL
721+
722+
Used when an invalid `options.protocol` is passed.
723+
707724
<a id="ERR_INVALID_REPL_EVAL_CONFIG"></a>
708725
### ERR_INVALID_REPL_EVAL_CONFIG
709726

@@ -879,6 +896,11 @@ Used to identify a specific kind of internal Node.js error that should not
879896
typically be triggered by user code. Instances of this error point to an
880897
internal bug within the Node.js binary itself.
881898

899+
<a id="ERR_UNESCAPED_CHARACTERS"></a>
900+
### ERR_UNESCAPED_CHARACTERS
901+
902+
Used when a string that contains unescaped characters was received.
903+
882904
<a id="ERR_UNKNOWN_ENCODING"></a>
883905
### ERR_UNKNOWN_ENCODING
884906

lib/_http_client.js

+13-14
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const Buffer = require('buffer').Buffer;
3737
const { urlToOptions, searchParamsSymbol } = require('internal/url');
3838
const outHeadersKey = require('internal/http').outHeadersKey;
3939
const nextTick = require('internal/process/next_tick').nextTick;
40+
const errors = require('internal/errors');
4041

4142
// The actual list of disallowed characters in regexp form is more like:
4243
// /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/
@@ -68,8 +69,8 @@ function isInvalidPath(s) {
6869

6970
function validateHost(host, name) {
7071
if (host != null && typeof host !== 'string') {
71-
throw new TypeError(
72-
`"options.${name}" must either be a string, undefined or null`);
72+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', `options.${name}`,
73+
['string', 'undefined', 'null'], host);
7374
}
7475
return host;
7576
}
@@ -80,7 +81,7 @@ function ClientRequest(options, cb) {
8081
if (typeof options === 'string') {
8182
options = url.parse(options);
8283
if (!options.hostname) {
83-
throw new Error('Unable to determine the domain name');
84+
throw new errors.Error('ERR_INVALID_DOMAIN_NAME');
8485
}
8586
} else if (options && options[searchParamsSymbol] &&
8687
options[searchParamsSymbol][searchParamsSymbol]) {
@@ -101,9 +102,8 @@ function ClientRequest(options, cb) {
101102
// Explicitly pass through this statement as agent will not be used
102103
// when createConnection is provided.
103104
} else if (typeof agent.addRequest !== 'function') {
104-
throw new TypeError(
105-
'Agent option must be an Agent-like object, undefined, or false.'
106-
);
105+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'Agent option',
106+
['Agent-like object', 'undefined', 'false']);
107107
}
108108
this.agent = agent;
109109

@@ -122,12 +122,11 @@ function ClientRequest(options, cb) {
122122
invalidPath = /[\u0000-\u0020]/.test(path);
123123
}
124124
if (invalidPath)
125-
throw new TypeError('Request path contains unescaped characters');
125+
throw new errors.TypeError('ERR_UNESCAPED_CHARACTERS', 'Request path');
126126
}
127127

128128
if (protocol !== expectedProtocol) {
129-
throw new Error('Protocol "' + protocol + '" not supported. ' +
130-
'Expected "' + expectedProtocol + '"');
129+
throw new errors.Error('ERR_INVALID_PROTOCOL', protocol, expectedProtocol);
131130
}
132131

133132
var defaultPort = options.defaultPort ||
@@ -145,12 +144,13 @@ function ClientRequest(options, cb) {
145144
var method = options.method;
146145
var methodIsString = (typeof method === 'string');
147146
if (method != null && !methodIsString) {
148-
throw new TypeError('Method must be a string');
147+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'method',
148+
'string', method);
149149
}
150150

151151
if (methodIsString && method) {
152152
if (!common._checkIsHttpToken(method)) {
153-
throw new TypeError('Method must be a valid HTTP token');
153+
throw new errors.TypeError('ERR_INVALID_HTTP_TOKEN', 'Method');
154154
}
155155
method = this.method = method.toUpperCase();
156156
} else {
@@ -211,8 +211,7 @@ function ClientRequest(options, cb) {
211211
options.headers);
212212
} else if (this.getHeader('expect')) {
213213
if (this._header) {
214-
throw new Error('Can\'t render headers after they are sent to the ' +
215-
'client');
214+
throw new errors.Error('ERR_HTTP_HEADERS_SENT');
216215
}
217216

218217
this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n',
@@ -303,7 +302,7 @@ ClientRequest.prototype._finish = function _finish() {
303302

304303
ClientRequest.prototype._implicitHeader = function _implicitHeader() {
305304
if (this._header) {
306-
throw new Error('Can\'t render headers after they are sent to the client');
305+
throw new errors.Error('ERR_HTTP_HEADERS_SENT');
307306
}
308307
this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n',
309308
this[outHeadersKey]);

lib/internal/errors.js

+6
Original file line numberDiff line numberDiff line change
@@ -130,18 +130,22 @@ E('ERR_INVALID_CALLBACK', 'Callback must be a function');
130130
E('ERR_INVALID_CHAR', 'Invalid character in %s');
131131
E('ERR_INVALID_CURSOR_POS',
132132
'Cannot set cursor row without setting its column');
133+
E('ERR_INVALID_DOMAIN_NAME', 'Unable to determine the domain name');
133134
E('ERR_INVALID_FD', '"fd" must be a positive integer: %s');
134135
E('ERR_INVALID_FILE_URL_HOST',
135136
'File URL host must be "localhost" or empty on %s');
136137
E('ERR_INVALID_FILE_URL_PATH', 'File URL path %s');
137138
E('ERR_INVALID_HANDLE_TYPE', 'This handle type cannot be sent');
139+
E('ERR_INVALID_HTTP_TOKEN', (name) => `${name} must be a valid HTTP token`);
138140
E('ERR_INVALID_IP_ADDRESS', 'Invalid IP address: %s');
139141
E('ERR_INVALID_OPT_VALUE',
140142
(name, value) => {
141143
return `The value "${String(value)}" is invalid for option "${name}"`;
142144
});
143145
E('ERR_INVALID_OPT_VALUE_ENCODING',
144146
(value) => `The value "${String(value)}" is invalid for option "encoding"`);
147+
E('ERR_INVALID_PROTOCOL', (protocol, expectedProtocol) =>
148+
`Protocol "${protocol}" not supported. Expected "${expectedProtocol}"`);
145149
E('ERR_INVALID_REPL_EVAL_CONFIG',
146150
'Cannot specify both "breakEvalOnSigint" and "eval" for REPL');
147151
E('ERR_INVALID_SYNC_FORK_INPUT',
@@ -185,6 +189,8 @@ E('ERR_TRANSFORM_ALREADY_TRANSFORMING',
185189
'Calling transform done when still transforming');
186190
E('ERR_TRANSFORM_WITH_LENGTH_0',
187191
'Calling transform done when writableState.length != 0');
192+
E('ERR_UNESCAPED_CHARACTERS',
193+
(name) => `${name} contains unescaped characters`);
188194
E('ERR_UNKNOWN_ENCODING', 'Unknown encoding: %s');
189195
E('ERR_UNKNOWN_SIGNAL', 'Unknown signal: %s');
190196
E('ERR_UNKNOWN_STDIN_TYPE', 'Unknown stdin file type');

test/parallel/test-http-client-check-http-token.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ server.listen(0, common.mustCall(() => {
2020
expectedFails.forEach((method) => {
2121
assert.throws(() => {
2222
http.request({ method, path: '/' }, common.mustNotCall());
23-
}, /^TypeError: Method must be a string$/);
23+
}, common.expectsError({
24+
code: 'ERR_INVALID_ARG_TYPE',
25+
type: TypeError,
26+
message: 'The "method" argument must be of type string. ' +
27+
`Received type ${typeof method}`
28+
}));
2429
});
2530

2631
expectedSuccesses.forEach((method) => {

test/parallel/test-http-client-reject-unexpected-agent.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,12 @@ server.listen(0, baseOptions.host, common.mustCall(function() {
4949
failingAgentOptions.forEach((agent) => {
5050
assert.throws(
5151
() => createRequest(agent),
52-
/^TypeError: Agent option must be an Agent-like object/,
53-
`Expected typeof agent: ${typeof agent} to be rejected`
52+
common.expectsError({
53+
code: 'ERR_INVALID_ARG_TYPE',
54+
type: TypeError,
55+
message: 'The "Agent option" argument must be one of type ' +
56+
'Agent-like object, undefined, or false'
57+
})
5458
);
5559
});
5660

test/parallel/test-http-client-unescaped-path.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,14 @@ const common = require('../common');
2424
const assert = require('assert');
2525
const http = require('http');
2626

27-
const errMessage = /contains unescaped characters/;
2827
for (let i = 0; i <= 32; i += 1) {
2928
const path = `bad${String.fromCharCode(i)}path`;
30-
assert.throws(() => http.get({ path }, common.mustNotCall()), errMessage);
29+
assert.throws(
30+
() => http.get({ path }, common.mustNotCall()),
31+
common.expectsError({
32+
code: 'ERR_UNESCAPED_CHARACTERS',
33+
type: TypeError,
34+
message: 'Request path contains unescaped characters'
35+
})
36+
);
3137
}

test/parallel/test-http-hostname-typechecking.js

+22-8
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,35 @@
11
'use strict';
2-
require('../common');
32

3+
const common = require('../common');
44
const assert = require('assert');
55
const http = require('http');
66

77
// All of these values should cause http.request() to throw synchronously
88
// when passed as the value of either options.hostname or options.host
99
const vals = [{}, [], NaN, Infinity, -Infinity, true, false, 1, 0, new Date()];
1010

11-
const errHostname =
12-
/^TypeError: "options\.hostname" must either be a string, undefined or null$/;
13-
const errHost =
14-
/^TypeError: "options\.host" must either be a string, undefined or null$/;
15-
1611
vals.forEach((v) => {
17-
assert.throws(() => http.request({ hostname: v }), errHostname);
18-
assert.throws(() => http.request({ host: v }), errHost);
12+
assert.throws(
13+
() => http.request({ hostname: v }),
14+
common.expectsError({
15+
code: 'ERR_INVALID_ARG_TYPE',
16+
type: TypeError,
17+
message: 'The "options.hostname" property must be one of ' +
18+
'type string, undefined, or null. ' +
19+
`Received type ${typeof v}`
20+
})
21+
);
22+
23+
assert.throws(
24+
() => http.request({ host: v }),
25+
common.expectsError({
26+
code: 'ERR_INVALID_ARG_TYPE',
27+
type: TypeError,
28+
message: 'The "options.host" property must be one of ' +
29+
'type string, undefined, or null. ' +
30+
`Received type ${typeof v}`
31+
})
32+
);
1933
});
2034

2135
// These values are OK and should not throw synchronously.

test/parallel/test-http-invalid-path-chars.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
'use strict';
2-
require('../common');
2+
3+
const common = require('../common');
34
const assert = require('assert');
45
const http = require('http');
56

6-
const expectedError = /^TypeError: Request path contains unescaped characters$/;
7+
const expectedError = common.expectsError({
8+
code: 'ERR_UNESCAPED_CHARACTERS',
9+
type: TypeError,
10+
message: 'Request path contains unescaped characters'
11+
}, 1320);
712
const theExperimentallyDeterminedNumber = 39;
813

914
function fail(path) {

test/parallel/test-http-request-invalid-method-error.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ const assert = require('assert');
44
const http = require('http');
55

66
assert.throws(
7-
() => { http.request({ method: '\0' }); },
8-
common.expectsError({ type: TypeError,
9-
message: 'Method must be a valid HTTP token' })
7+
() => http.request({ method: '\0' }),
8+
common.expectsError({
9+
code: 'ERR_INVALID_HTTP_TOKEN',
10+
type: TypeError,
11+
message: 'Method must be a valid HTTP token'
12+
})
1013
);

test/parallel/test-internal-errors.js

+25
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,28 @@ assert.throws(
235235
assert.strictEqual(
236236
errors.message('ERR_TLS_CERT_ALTNAME_INVALID', ['altname']),
237237
'Hostname/IP does not match certificate\'s altnames: altname');
238+
239+
assert.strictEqual(
240+
errors.message('ERR_INVALID_PROTOCOL', ['bad protocol', 'http']),
241+
'Protocol "bad protocol" not supported. Expected "http"'
242+
);
243+
244+
assert.strictEqual(
245+
errors.message('ERR_HTTP_HEADERS_SENT'),
246+
'Cannot render headers after they are sent to the client'
247+
);
248+
249+
assert.strictEqual(
250+
errors.message('ERR_INVALID_DOMAIN_NAME'),
251+
'Unable to determine the domain name'
252+
);
253+
254+
assert.strictEqual(
255+
errors.message('ERR_INVALID_HTTP_TOKEN', ['Method']),
256+
'Method must be a valid HTTP token'
257+
);
258+
259+
assert.strictEqual(
260+
errors.message('ERR_UNESCAPED_CHARACTERS', ['Request path']),
261+
'Request path contains unescaped characters'
262+
);

0 commit comments

Comments
 (0)