http: improve parser error messages
Include the library-provided reason in the Error’s `message`. Fixes: https://github.com/nodejs/node/issues/28468 PR-URL: https://github.com/nodejs/node/pull/28487 Reviewed-By: Anto Aravinth <anto.aravinth.cse@gmail.com> Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
This commit is contained in:
parent
fd23c12263
commit
ba565a3734
@ -33,6 +33,7 @@ const {
|
|||||||
httpSocketSetup,
|
httpSocketSetup,
|
||||||
parsers,
|
parsers,
|
||||||
HTTPParser,
|
HTTPParser,
|
||||||
|
prepareError,
|
||||||
} = require('_http_common');
|
} = require('_http_common');
|
||||||
const { OutgoingMessage } = require('_http_outgoing');
|
const { OutgoingMessage } = require('_http_outgoing');
|
||||||
const Agent = require('_http_agent');
|
const Agent = require('_http_agent');
|
||||||
@ -451,6 +452,7 @@ function socketOnData(d) {
|
|||||||
|
|
||||||
const ret = parser.execute(d);
|
const ret = parser.execute(d);
|
||||||
if (ret instanceof Error) {
|
if (ret instanceof Error) {
|
||||||
|
prepareError(ret, parser, d);
|
||||||
debug('parse error', ret);
|
debug('parse error', ret);
|
||||||
freeParser(parser, req, socket);
|
freeParser(parser, req, socket);
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
|
@ -239,6 +239,12 @@ function cleanParser(parser) {
|
|||||||
parser._consumed = false;
|
parser._consumed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function prepareError(err, parser, rawPacket) {
|
||||||
|
err.rawPacket = rawPacket || parser.getCurrentBuffer();
|
||||||
|
if (typeof err.reason === 'string')
|
||||||
|
err.message = `Parse Error: ${err.reason}`;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
_checkInvalidHeaderChar: checkInvalidHeaderChar,
|
_checkInvalidHeaderChar: checkInvalidHeaderChar,
|
||||||
_checkIsHttpToken: checkIsHttpToken,
|
_checkIsHttpToken: checkIsHttpToken,
|
||||||
@ -251,5 +257,6 @@ module.exports = {
|
|||||||
methods,
|
methods,
|
||||||
parsers,
|
parsers,
|
||||||
kIncomingMessage,
|
kIncomingMessage,
|
||||||
HTTPParser
|
HTTPParser,
|
||||||
|
prepareError,
|
||||||
};
|
};
|
||||||
|
@ -35,7 +35,8 @@ const {
|
|||||||
httpSocketSetup,
|
httpSocketSetup,
|
||||||
kIncomingMessage,
|
kIncomingMessage,
|
||||||
HTTPParser,
|
HTTPParser,
|
||||||
_checkInvalidHeaderChar: checkInvalidHeaderChar
|
_checkInvalidHeaderChar: checkInvalidHeaderChar,
|
||||||
|
prepareError,
|
||||||
} = require('_http_common');
|
} = require('_http_common');
|
||||||
const { OutgoingMessage } = require('_http_outgoing');
|
const { OutgoingMessage } = require('_http_outgoing');
|
||||||
const { outHeadersKey, ondrain, nowDate } = require('internal/http');
|
const { outHeadersKey, ondrain, nowDate } = require('internal/http');
|
||||||
@ -550,6 +551,7 @@ function onParserExecuteCommon(server, socket, parser, state, ret, d) {
|
|||||||
resetSocketTimeout(server, socket, state);
|
resetSocketTimeout(server, socket, state);
|
||||||
|
|
||||||
if (ret instanceof Error) {
|
if (ret instanceof Error) {
|
||||||
|
prepareError(ret, parser, d);
|
||||||
ret.rawPacket = d || parser.getCurrentBuffer();
|
ret.rawPacket = d || parser.getCurrentBuffer();
|
||||||
debug('parse error', ret);
|
debug('parse error', ret);
|
||||||
socketOnError.call(socket, ret);
|
socketOnError.call(socket, ret);
|
||||||
|
32
test/parallel/test-http-client-error-rawbytes.js
Normal file
32
test/parallel/test-http-client-error-rawbytes.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
'use strict';
|
||||||
|
const common = require('../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
const http = require('http');
|
||||||
|
const net = require('net');
|
||||||
|
|
||||||
|
const response = Buffer.from('HTTP/1.1 200 OK\r\n' +
|
||||||
|
'Content-Length: 6\r\n' +
|
||||||
|
'Transfer-Encoding: Chunked\r\n' +
|
||||||
|
'\r\n' +
|
||||||
|
'6\r\nfoobar' +
|
||||||
|
'0\r\n');
|
||||||
|
|
||||||
|
const server = net.createServer(common.mustCall((conn) => {
|
||||||
|
conn.write(response);
|
||||||
|
}));
|
||||||
|
|
||||||
|
server.listen(0, common.mustCall(() => {
|
||||||
|
const req = http.get(`http://localhost:${server.address().port}/`);
|
||||||
|
req.end();
|
||||||
|
req.on('error', common.mustCall((err) => {
|
||||||
|
const reason = 'Content-Length can\'t be present with chunked encoding';
|
||||||
|
assert.strictEqual(err.message, `Parse Error: ${reason}`);
|
||||||
|
assert(err.bytesParsed < response.length);
|
||||||
|
assert(err.bytesParsed >= response.indexOf('Transfer-Encoding'));
|
||||||
|
assert.strictEqual(err.code, 'HPE_UNEXPECTED_CONTENT_LENGTH');
|
||||||
|
assert.strictEqual(err.reason, reason);
|
||||||
|
assert.deepStrictEqual(err.rawPacket, response);
|
||||||
|
|
||||||
|
server.close();
|
||||||
|
}));
|
||||||
|
}));
|
@ -44,7 +44,7 @@ server.listen(0, common.mustCall(() => {
|
|||||||
}).on('error', common.mustCall((e) => {
|
}).on('error', common.mustCall((e) => {
|
||||||
common.expectsError({
|
common.expectsError({
|
||||||
code: 'HPE_INVALID_CONSTANT',
|
code: 'HPE_INVALID_CONSTANT',
|
||||||
message: 'Parse Error'
|
message: 'Parse Error: Expected HTTP/'
|
||||||
})(e);
|
})(e);
|
||||||
countdown.dec();
|
countdown.dec();
|
||||||
}));
|
}));
|
||||||
|
@ -19,7 +19,7 @@ const server = createServer();
|
|||||||
server.on('connection', mustCall((socket) => {
|
server.on('connection', mustCall((socket) => {
|
||||||
socket.on('error', expectsError({
|
socket.on('error', expectsError({
|
||||||
type: Error,
|
type: Error,
|
||||||
message: 'Parse Error',
|
message: 'Parse Error: Header overflow',
|
||||||
code: 'HPE_HEADER_OVERFLOW',
|
code: 'HPE_HEADER_OVERFLOW',
|
||||||
bytesParsed: maxHeaderSize + PAYLOAD_GET.length,
|
bytesParsed: maxHeaderSize + PAYLOAD_GET.length,
|
||||||
rawPacket: Buffer.from(PAYLOAD)
|
rawPacket: Buffer.from(PAYLOAD)
|
||||||
|
@ -13,7 +13,7 @@ server.on('clientError', common.mustCall(function(err, socket) {
|
|||||||
assert.strictEqual(err instanceof Error, true);
|
assert.strictEqual(err instanceof Error, true);
|
||||||
assert.strictEqual(err.code, 'HPE_INVALID_METHOD');
|
assert.strictEqual(err.code, 'HPE_INVALID_METHOD');
|
||||||
assert.strictEqual(err.bytesParsed, 1);
|
assert.strictEqual(err.bytesParsed, 1);
|
||||||
assert.strictEqual(err.message, 'Parse Error');
|
assert.strictEqual(err.message, 'Parse Error: Invalid method encountered');
|
||||||
assert.strictEqual(err.rawPacket.toString(), 'Oopsie-doopsie\r\n');
|
assert.strictEqual(err.rawPacket.toString(), 'Oopsie-doopsie\r\n');
|
||||||
|
|
||||||
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
|
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
|
||||||
|
@ -14,7 +14,7 @@ const server = createServer();
|
|||||||
server.on('connection', mustCall((socket) => {
|
server.on('connection', mustCall((socket) => {
|
||||||
socket.on('error', expectsError({
|
socket.on('error', expectsError({
|
||||||
type: Error,
|
type: Error,
|
||||||
message: 'Parse Error',
|
message: 'Parse Error: Invalid method encountered',
|
||||||
code: 'HPE_INVALID_METHOD',
|
code: 'HPE_INVALID_METHOD',
|
||||||
bytesParsed: 0,
|
bytesParsed: 0,
|
||||||
rawPacket: Buffer.from('FOO /\r\n')
|
rawPacket: Buffer.from('FOO /\r\n')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user