From f7d128ad481bbdde204d69b73d7e93e03f557c9e Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Tue, 3 Sep 2019 09:41:15 +0200 Subject: [PATCH] http: implement capture rejections for 'request' event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/27867 Reviewed-By: Benjamin Gruenbaum Reviewed-By: James M Snell Reviewed-By: Jeremiah Senkpiel Reviewed-By: Anna Henningsen Reviewed-By: Michaƫl Zasso --- lib/_http_server.js | 21 ++++ .../test-http-server-capture-rejections.js | 108 ++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 test/parallel/test-http-server-capture-rejections.js diff --git a/lib/_http_server.js b/lib/_http_server.js index 3520fb9c41a8c0..f495f70162732c 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -29,6 +29,7 @@ const { } = primordials; const net = require('net'); +const EE = require('events'); const assert = require('internal/assert'); const { parsers, @@ -366,6 +367,26 @@ Server.prototype.setTimeout = function setTimeout(msecs, callback) { return this; }; +Server.prototype[EE.captureRejectionSymbol] = function( + err, event, req, res) { + + switch (event) { + case 'request': + if (!res.headersSent && !res.writableEnded) { + // Don't leak headers. + for (const name of res.getHeaderNames()) { + res.removeHeader(name); + } + res.statusCode = 500; + res.end(STATUS_CODES[500]); + } else { + res.destroy(); + } + break; + default: + this.emit('error', err); + } +}; function connectionListener(socket) { defaultTriggerAsyncIdScope( diff --git a/test/parallel/test-http-server-capture-rejections.js b/test/parallel/test-http-server-capture-rejections.js new file mode 100644 index 00000000000000..b437e27b589dfc --- /dev/null +++ b/test/parallel/test-http-server-capture-rejections.js @@ -0,0 +1,108 @@ +'use strict'; + +const common = require('../common'); +const events = require('events'); +const { createServer, request } = require('http'); +const assert = require('assert'); + +events.captureRejections = true; + +{ + const server = createServer(common.mustCall(async (req, res) => { + // We will test that this header is cleaned up before forwarding. + res.setHeader('content-type', 'application/json'); + throw new Error('kaboom'); + })); + + server.listen(0, common.mustCall(() => { + const req = request({ + method: 'GET', + host: server.address().host, + port: server.address().port + }); + + req.end(); + + req.on('response', common.mustCall((res) => { + assert.strictEqual(res.statusCode, 500); + assert.strictEqual(res.headers.hasOwnProperty('content-type'), false); + let data = ''; + res.setEncoding('utf8'); + res.on('data', common.mustCall((chunk) => { + data += chunk; + })); + res.on('end', common.mustCall(() => { + assert.strictEqual(data, 'Internal Server Error'); + server.close(); + })); + })); + })); +} + +{ + let resolve; + const latch = new Promise((_resolve) => { + resolve = _resolve; + }); + const server = createServer(common.mustCall(async (req, res) => { + server.close(); + + // We will test that this header is cleaned up before forwarding. + res.setHeader('content-type', 'application/json'); + res.write('{'); + req.resume(); + + // Wait so the data is on the wire + await latch; + + throw new Error('kaboom'); + })); + + server.listen(0, common.mustCall(() => { + const req = request({ + method: 'GET', + host: server.address().host, + port: server.address().port + }); + + req.end(); + + req.on('response', common.mustCall((res) => { + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers['content-type'], 'application/json'); + resolve(); + + let data = ''; + res.setEncoding('utf8'); + res.on('data', common.mustCall((chunk) => { + data += chunk; + })); + + req.on('close', common.mustCall(() => { + assert.strictEqual(data, '{'); + })); + })); + })); +} + +{ + const server = createServer(common.mustCall(async (req, res) => { + // We will test that this header is cleaned up before forwarding. + res.writeHead(200); + throw new Error('kaboom'); + })); + + server.listen(0, common.mustCall(() => { + const req = request({ + method: 'GET', + host: server.address().host, + port: server.address().port + }); + + req.end(); + req.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'ECONNRESET'); + server.close(); + })); + })); +}