errors: migrate _http_outgoing

PR-URL: https://github.com/nodejs/node/pull/14735
Refs: https://github.com/nodejs/node/issues/11273
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
This commit is contained in:
Weijia Wang 2017-08-10 18:58:22 +08:00 коммит произвёл Refael Ackermann
Родитель 28a47aa1bb
Коммит 11a2ca29ba
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: CD704BD80FDDDB64
14 изменённых файлов: 201 добавлений и 96 удалений

Просмотреть файл

@ -998,6 +998,11 @@ Used when an attempt is made to open an IPC communication channel with a
synchronous forked Node.js process. See the documentation for the
[`child_process`](child_process.html) module for more information.
<a id="ERR_METHOD_NOT_IMPLEMENTED"></a>
### ERR_METHOD_NOT_IMPLEMENTED
Used when a method is required but not implemented.
<a id="ERR_MISSING_ARGS"></a>
### ERR_MISSING_ARGS

Просмотреть файл

@ -150,7 +150,7 @@ function ClientRequest(options, cb) {
if (methodIsString && method) {
if (!common._checkIsHttpToken(method)) {
throw new errors.TypeError('ERR_INVALID_HTTP_TOKEN', 'Method');
throw new errors.TypeError('ERR_INVALID_HTTP_TOKEN', 'Method', method);
}
method = this.method = method.toUpperCase();
} else {
@ -211,7 +211,7 @@ function ClientRequest(options, cb) {
options.headers);
} else if (this.getHeader('expect')) {
if (this._header) {
throw new errors.Error('ERR_HTTP_HEADERS_SENT');
throw new errors.Error('ERR_HTTP_HEADERS_SENT', 'render');
}
this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n',
@ -302,7 +302,7 @@ ClientRequest.prototype._finish = function _finish() {
ClientRequest.prototype._implicitHeader = function _implicitHeader() {
if (this._header) {
throw new errors.Error('ERR_HTTP_HEADERS_SENT');
throw new errors.Error('ERR_HTTP_HEADERS_SENT', 'render');
}
this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n',
this[outHeadersKey]);

Просмотреть файл

@ -163,7 +163,7 @@ Object.defineProperty(OutgoingMessage.prototype, '_headerNames', {
OutgoingMessage.prototype._renderHeaders = function _renderHeaders() {
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', 'render');
}
var headersMap = this[outHeadersKey];
@ -433,14 +433,14 @@ function _storeHeader(firstLine, headers) {
function storeHeader(self, state, key, value, validate) {
if (validate) {
if (typeof key !== 'string' || !key || !checkIsHttpToken(key)) {
throw new TypeError(
'Header name must be a valid HTTP Token ["' + key + '"]');
throw new errors.TypeError(
'ERR_INVALID_HTTP_TOKEN', 'Header name', key);
}
if (value === undefined) {
throw new Error('Header "%s" value must not be undefined', key);
throw new errors.TypeError('ERR_MISSING_ARGS', `header "${key}"`);
} else if (checkInvalidHeaderChar(value)) {
debug('Header "%s" contains invalid characters', key);
throw new TypeError('The header content contains invalid characters');
throw new errors.TypeError('ERR_INVALID_CHAR', 'header content', key);
}
}
state.header += key + ': ' + escapeHeaderValue(value) + CRLF;
@ -491,14 +491,14 @@ function matchHeader(self, state, field, value) {
function validateHeader(msg, name, value) {
if (typeof name !== 'string' || !name || !checkIsHttpToken(name))
throw new TypeError(`Header name must be a valid HTTP Token ["${name}"]`);
throw new errors.TypeError('ERR_INVALID_HTTP_TOKEN', 'Header name', name);
if (value === undefined)
throw new Error('"value" required in setHeader("' + name + '", value)');
throw new errors.TypeError('ERR_MISSING_ARGS', 'value');
if (msg._header)
throw new Error('Can\'t set headers after they are sent.');
throw new errors.Error('ERR_HTTP_HEADERS_SENT', 'set');
if (checkInvalidHeaderChar(value)) {
debug('Header "%s" contains invalid characters', name);
throw new TypeError('The header content contains invalid characters');
throw new errors.TypeError('ERR_INVALID_CHAR', 'header content', name);
}
}
OutgoingMessage.prototype.setHeader = function setHeader(name, value) {
@ -529,7 +529,7 @@ OutgoingMessage.prototype.setHeader = function setHeader(name, value) {
OutgoingMessage.prototype.getHeader = function getHeader(name) {
if (typeof name !== 'string') {
throw new TypeError('"name" argument must be a string');
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'name', 'string');
}
if (!this[outHeadersKey]) return;
@ -565,7 +565,7 @@ OutgoingMessage.prototype.getHeaders = function getHeaders() {
OutgoingMessage.prototype.hasHeader = function hasHeader(name) {
if (typeof name !== 'string') {
throw new TypeError('"name" argument must be a string');
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'name', 'string');
}
return !!(this[outHeadersKey] && this[outHeadersKey][name.toLowerCase()]);
@ -574,11 +574,11 @@ OutgoingMessage.prototype.hasHeader = function hasHeader(name) {
OutgoingMessage.prototype.removeHeader = function removeHeader(name) {
if (typeof name !== 'string') {
throw new TypeError('"name" argument must be a string');
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'name', 'string');
}
if (this._header) {
throw new Error('Can\'t remove headers after they are sent');
throw new errors.Error('ERR_HTTP_HEADERS_SENT', 'remove');
}
var key = name.toLowerCase();
@ -609,7 +609,7 @@ OutgoingMessage.prototype.removeHeader = function removeHeader(name) {
OutgoingMessage.prototype._implicitHeader = function _implicitHeader() {
throw new Error('_implicitHeader() method is not implemented');
throw new errors.Error('ERR_METHOD_NOT_IMPLEMENTED', '_implicitHeader()');
};
Object.defineProperty(OutgoingMessage.prototype, 'headersSent', {
@ -646,7 +646,8 @@ function write_(msg, chunk, encoding, callback, fromEnd) {
}
if (!fromEnd && typeof chunk !== 'string' && !(chunk instanceof Buffer)) {
throw new TypeError('First argument must be a string or Buffer');
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'first argument',
['string', 'buffer']);
}
@ -712,12 +713,12 @@ OutgoingMessage.prototype.addTrailers = function addTrailers(headers) {
value = headers[key];
}
if (typeof field !== 'string' || !field || !checkIsHttpToken(field)) {
throw new TypeError(
'Trailer name must be a valid HTTP Token ["' + field + '"]');
throw new errors.TypeError('ERR_INVALID_HTTP_TOKEN', 'Trailer name',
field);
}
if (checkInvalidHeaderChar(value)) {
debug('Trailer "%s" contains invalid characters', field);
throw new TypeError('The trailer content contains invalid characters');
throw new errors.TypeError('ERR_INVALID_CHAR', 'trailer content', field);
}
this._trailer += field + ': ' + escapeHeaderValue(value) + CRLF;
}
@ -743,7 +744,8 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
var uncork;
if (chunk) {
if (typeof chunk !== 'string' && !(chunk instanceof Buffer)) {
throw new TypeError('First argument must be a string or Buffer');
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'first argument',
['string', 'buffer']);
}
if (!this._header) {
if (typeof chunk === 'string')

Просмотреть файл

@ -214,7 +214,7 @@ function writeHead(statusCode, reason, obj) {
}
}
if (k === undefined && this._header) {
throw new errors.Error('ERR_HTTP_HEADERS_SENT');
throw new errors.Error('ERR_HTTP_HEADERS_SENT', 'render');
}
// only progressive api is used
headers = this[outHeadersKey];

Просмотреть файл

@ -114,7 +114,7 @@ E('ERR_ENCODING_NOT_SUPPORTED',
E('ERR_ENCODING_INVALID_ENCODED_DATA',
(enc) => `The encoded data was not valid for encoding ${enc}`);
E('ERR_HTTP_HEADERS_SENT',
'Cannot render headers after they are sent to the client');
'Cannot %s headers after they are sent to the client');
E('ERR_HTTP_INVALID_STATUS_CODE', 'Invalid status code: %s');
E('ERR_HTTP_TRAILER_INVALID',
'Trailers are invalid with this transfer encoding');
@ -190,7 +190,7 @@ E('ERR_INVALID_ARRAY_LENGTH',
});
E('ERR_INVALID_BUFFER_SIZE', 'Buffer size must be a multiple of %s');
E('ERR_INVALID_CALLBACK', 'Callback must be a function');
E('ERR_INVALID_CHAR', 'Invalid character in %s');
E('ERR_INVALID_CHAR', invalidChar);
E('ERR_INVALID_CURSOR_POS',
'Cannot set cursor row without setting its column');
E('ERR_INVALID_DOMAIN_NAME', 'Unable to determine the domain name');
@ -199,7 +199,7 @@ 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_HTTP_TOKEN', '%s must be a valid HTTP token ["%s"]');
E('ERR_INVALID_IP_ADDRESS', 'Invalid IP address: %s');
E('ERR_INVALID_OPT_VALUE',
(name, value) => {
@ -222,6 +222,7 @@ E('ERR_IPC_CHANNEL_CLOSED', 'Channel closed');
E('ERR_IPC_DISCONNECTED', 'IPC channel is already disconnected');
E('ERR_IPC_ONE_PIPE', 'Child process can have only one IPC pipe');
E('ERR_IPC_SYNC_FORK', 'IPC cannot be used with synchronous forks');
E('ERR_METHOD_NOT_IMPLEMENTED', 'The %s method is not implemented');
E('ERR_MISSING_ARGS', missingArgs);
E('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times');
E('ERR_NAPI_CONS_FUNCTION', 'Constructor must be a function');
@ -342,3 +343,11 @@ function bufferOutOfBounds(name, isWriting) {
return `"${name}" is outside of buffer bounds`;
}
}
function invalidChar(name, field) {
let msg = `Invalid character in ${name}`;
if (field) {
msg += ` ["${field}"]`;
}
return msg;
}

Просмотреть файл

@ -50,18 +50,38 @@ const s = http.createServer(common.mustCall((req, res) => {
assert.deepStrictEqual(res.hasHeader('Connection'), false);
assert.deepStrictEqual(res.getHeader('Connection'), undefined);
assert.throws(() => {
res.setHeader();
}, /^TypeError: Header name must be a valid HTTP Token \["undefined"\]$/);
assert.throws(() => {
res.setHeader('someHeader');
}, /^Error: "value" required in setHeader\("someHeader", value\)$/);
assert.throws(() => {
res.getHeader();
}, /^TypeError: "name" argument must be a string$/);
assert.throws(() => {
res.removeHeader();
}, /^TypeError: "name" argument must be a string$/);
common.expectsError(
() => res.setHeader(),
{
code: 'ERR_INVALID_HTTP_TOKEN',
type: TypeError,
message: 'Header name must be a valid HTTP token ["undefined"]'
}
);
common.expectsError(
() => res.setHeader('someHeader'),
{
code: 'ERR_MISSING_ARGS',
type: TypeError,
message: 'The "value" argument must be specified'
}
);
common.expectsError(
() => res.getHeader(),
{
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "name" argument must be of type string'
}
);
common.expectsError(
() => res.removeHeader(),
{
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "name" argument must be of type string'
}
);
const arrayValues = [1, 2, 3];
res.setHeader('x-test-header', 'testing');
@ -89,18 +109,23 @@ const s = http.createServer(common.mustCall((req, res) => {
assert.strictEqual(res.hasHeader('x-test-header2'), true);
assert.strictEqual(res.hasHeader('X-TEST-HEADER2'), true);
assert.strictEqual(res.hasHeader('X-Test-Header2'), true);
assert.throws(() => {
res.hasHeader();
}, /^TypeError: "name" argument must be a string$/);
assert.throws(() => {
res.hasHeader(null);
}, /^TypeError: "name" argument must be a string$/);
assert.throws(() => {
res.hasHeader(true);
}, /^TypeError: "name" argument must be a string$/);
assert.throws(() => {
res.hasHeader({ toString: () => 'X-TEST-HEADER2' });
}, /^TypeError: "name" argument must be a string$/);
[
undefined,
null,
true,
{},
{ toString: () => 'X-TEST-HEADER2' },
() => { }
].forEach((val) => {
common.expectsError(
() => res.hasHeader(val),
{
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "name" argument must be of type string'
}
);
});
res.removeHeader('x-test-header2');

Просмотреть файл

@ -1,5 +1,5 @@
'use strict';
require('../common');
const common = require('../common');
const assert = require('assert');
const http = require('http');
@ -7,8 +7,14 @@ const OutgoingMessage = http.OutgoingMessage;
const ClientRequest = http.ClientRequest;
const ServerResponse = http.ServerResponse;
assert.throws(OutgoingMessage.prototype._implicitHeader,
/^Error: _implicitHeader\(\) method is not implemented$/);
common.expectsError(
OutgoingMessage.prototype._implicitHeader,
{
code: 'ERR_METHOD_NOT_IMPLEMENTED',
type: Error,
message: 'The _implicitHeader() method is not implemented'
}
);
assert.strictEqual(
typeof ClientRequest.prototype._implicitHeader, 'function');
assert.strictEqual(
@ -18,45 +24,77 @@ assert.strictEqual(
assert.throws(() => {
const outgoingMessage = new OutgoingMessage();
outgoingMessage.setHeader();
}, /^TypeError: Header name must be a valid HTTP Token \["undefined"\]$/);
}, common.expectsError({
code: 'ERR_INVALID_HTTP_TOKEN',
type: TypeError,
message: 'Header name must be a valid HTTP token ["undefined"]'
}));
assert.throws(() => {
const outgoingMessage = new OutgoingMessage();
outgoingMessage.setHeader('test');
}, /^Error: "value" required in setHeader\("test", value\)$/);
}, common.expectsError({
code: 'ERR_MISSING_ARGS',
type: TypeError,
message: 'The "value" argument must be specified'
}));
assert.throws(() => {
const outgoingMessage = new OutgoingMessage();
outgoingMessage.setHeader(404);
}, /^TypeError: Header name must be a valid HTTP Token \["404"\]$/);
}, common.expectsError({
code: 'ERR_INVALID_HTTP_TOKEN',
type: TypeError,
message: 'Header name must be a valid HTTP token ["404"]'
}));
assert.throws(() => {
const outgoingMessage = new OutgoingMessage();
outgoingMessage.setHeader.call({ _header: 'test' }, 'test', 'value');
}, /^Error: Can't set headers after they are sent\.$/);
}, common.expectsError({
code: 'ERR_HTTP_HEADERS_SENT',
type: Error,
message: 'Cannot set headers after they are sent to the client'
}));
assert.throws(() => {
const outgoingMessage = new OutgoingMessage();
outgoingMessage.setHeader('200', 'あ');
}, /^TypeError: The header content contains invalid characters$/);
}, common.expectsError({
code: 'ERR_INVALID_CHAR',
type: TypeError,
message: 'Invalid character in header content ["200"]'
}));
// write
assert.throws(() => {
const outgoingMessage = new OutgoingMessage();
outgoingMessage.write();
}, /^Error: _implicitHeader\(\) method is not implemented$/);
}, common.expectsError({
code: 'ERR_METHOD_NOT_IMPLEMENTED',
type: Error,
message: 'The _implicitHeader() method is not implemented'
}));
assert(OutgoingMessage.prototype.write.call({ _header: 'test' }));
assert.throws(() => {
const outgoingMessage = new OutgoingMessage();
outgoingMessage.write.call({ _header: 'test', _hasBody: 'test' });
}, /^TypeError: First argument must be a string or Buffer$/);
}, common.expectsError({
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The first argument must be one of type string or buffer'
}));
assert.throws(() => {
const outgoingMessage = new OutgoingMessage();
outgoingMessage.write.call({ _header: 'test', _hasBody: 'test' }, 1);
}, /^TypeError: First argument must be a string or Buffer$/);
}, common.expectsError({
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The first argument must be one of type string or buffer'
}));
// addTrailers
assert.throws(() => {
@ -67,9 +105,17 @@ assert.throws(() => {
assert.throws(() => {
const outgoingMessage = new OutgoingMessage();
outgoingMessage.addTrailers({ 'あ': 'value' });
}, /^TypeError: Trailer name must be a valid HTTP Token \["あ"\]$/);
}, common.expectsError({
code: 'ERR_INVALID_HTTP_TOKEN',
type: TypeError,
message: 'Trailer name must be a valid HTTP token ["あ"]'
}));
assert.throws(() => {
const outgoingMessage = new OutgoingMessage();
outgoingMessage.addTrailers({ 404: 'あ' });
}, /^TypeError: The trailer content contains invalid characters$/);
}, common.expectsError({
code: 'ERR_INVALID_CHAR',
type: TypeError,
message: 'Invalid character in trailer content ["404"]'
}));

Просмотреть файл

@ -1,7 +1,7 @@
'use strict';
// Flags: --expose-internals
require('../common');
const common = require('../common');
const assert = require('assert');
const outHeadersKey = require('internal/http').outHeadersKey;
@ -11,9 +11,13 @@ const OutgoingMessage = http.OutgoingMessage;
{
const outgoingMessage = new OutgoingMessage();
outgoingMessage._header = {};
assert.throws(
common.expectsError(
outgoingMessage._renderHeaders.bind(outgoingMessage),
/^Error: Can't render headers after they are sent to the client$/
{
code: 'ERR_HTTP_HEADERS_SENT',
type: Error,
message: 'Cannot render headers after they are sent to the client'
}
);
}

Просмотреть файл

@ -8,6 +8,6 @@ assert.throws(
common.expectsError({
code: 'ERR_INVALID_HTTP_TOKEN',
type: TypeError,
message: 'Method must be a valid HTTP token'
message: 'Method must be a valid HTTP token ["\u0000"]'
})
);

Просмотреть файл

@ -1,5 +1,5 @@
'use strict';
require('../common');
const common = require('../common');
const assert = require('assert');
const http = require('http');
@ -8,9 +8,14 @@ const server = http.createServer((req, res) => {
res.setHeader('header1', 1);
});
res.write('abc');
assert.throws(() => {
res.setHeader('header2', 2);
}, /Can't set headers after they are sent\./);
common.expectsError(
() => res.setHeader('header2', 2),
{
code: 'ERR_HTTP_HEADERS_SENT',
type: Error,
message: 'Cannot set headers after they are sent to the client'
}
);
res.end();
});

Просмотреть файл

@ -1,5 +1,5 @@
'use strict';
require('../common');
const common = require('../common');
const assert = require('assert');
const http = require('http');
@ -8,9 +8,14 @@ const server = http.createServer((req, res) => {
res.removeHeader('header1', 1);
});
res.write('abc');
assert.throws(() => {
res.removeHeader('header2', 2);
}, /Can't remove headers after they are sent/);
common.expectsError(
() => res.removeHeader('header2', 2),
{
code: 'ERR_HTTP_HEADERS_SENT',
type: Error,
message: 'Cannot remove headers after they are sent to the client'
}
);
res.end();
});

Просмотреть файл

@ -1,6 +1,6 @@
'use strict';
require('../common');
const common = require('../common');
const http = require('http');
const net = require('net');
const url = require('url');
@ -19,23 +19,29 @@ const y = 'foo⠊Set-Cookie: foo=bar';
let count = 0;
function test(res, code, header) {
assert.throws(() => {
res.writeHead(code, header);
}, /^TypeError: The header content contains invalid characters$/);
function test(res, code, key, value) {
const header = { [key]: value };
common.expectsError(
() => res.writeHead(code, header),
{
code: 'ERR_INVALID_CHAR',
type: TypeError,
message: `Invalid character in header content ["${key}"]`
}
);
}
const server = http.createServer((req, res) => {
switch (count++) {
case 0:
const loc = url.parse(req.url, true).query.lang;
test(res, 302, { Location: `/foo?lang=${loc}` });
test(res, 302, 'Location', `/foo?lang=${loc}`);
break;
case 1:
test(res, 200, { 'foo': x });
test(res, 200, 'foo', x);
break;
case 2:
test(res, 200, { 'foo': y });
test(res, 200, 'foo', y);
break;
default:
assert.fail('should not get to here.');

Просмотреть файл

@ -41,16 +41,14 @@ const s = http.createServer(common.mustCall((req, res) => {
assert.ok(threw, 'Non-string names should throw');
// undefined value should throw, via 979d0ca8
threw = false;
try {
res.setHeader('foo', undefined);
} catch (e) {
assert.ok(e instanceof Error);
assert.strictEqual(e.message,
'"value" required in setHeader("foo", value)');
threw = true;
}
assert.ok(threw, 'Undefined value should throw');
common.expectsError(
() => res.setHeader('foo', undefined),
{
code: 'ERR_MISSING_ARGS',
type: TypeError,
message: 'The "value" argument must be specified'
}
);
res.writeHead(200, { Test: '2' });

Просмотреть файл

@ -242,7 +242,7 @@ assert.strictEqual(
);
assert.strictEqual(
errors.message('ERR_HTTP_HEADERS_SENT'),
errors.message('ERR_HTTP_HEADERS_SENT', ['render']),
'Cannot render headers after they are sent to the client'
);
@ -252,8 +252,8 @@ assert.strictEqual(
);
assert.strictEqual(
errors.message('ERR_INVALID_HTTP_TOKEN', ['Method']),
'Method must be a valid HTTP token'
errors.message('ERR_INVALID_HTTP_TOKEN', ['Method', 'foo']),
'Method must be a valid HTTP token ["foo"]'
);
assert.strictEqual(