From 736a7d8d60acdcf3274ae160fadefbbbbe4e4311 Mon Sep 17 00:00:00 2001 From: Paolo Insogna Date: Mon, 27 Jun 2022 14:13:00 +0000 Subject: [PATCH] http: do not leak error listeners PR-URL: https://github.com/nodejs/node/pull/43587 Fixes: https://github.com/nodejs/node/issues/43548 Reviewed-By: Matteo Collina Reviewed-By: Luigi Pinca Reviewed-By: Ricky Zhou <0x19951125@gmail.com> Reviewed-By: Mohammed Keyvanzadeh --- lib/_http_server.js | 5 ++- test/parallel/test-http-socket-listeners.js | 44 +++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 test/parallel/test-http-socket-listeners.js diff --git a/lib/_http_server.js b/lib/_http_server.js index e908b1b1b3d..0eedace9ea4 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -763,7 +763,10 @@ const requestHeaderFieldsTooLargeResponse = Buffer.from( function socketOnError(e) { // Ignore further errors this.removeListener('error', socketOnError); - this.on('error', noop); + + if (this.listenerCount('error') === 0) { + this.on('error', noop); + } if (!this.server.emit('clientError', e, this)) { if (this.writable && this.bytesWritten === 0) { diff --git a/test/parallel/test-http-socket-listeners.js b/test/parallel/test-http-socket-listeners.js new file mode 100644 index 00000000000..2513bc1a90c --- /dev/null +++ b/test/parallel/test-http-socket-listeners.js @@ -0,0 +1,44 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const net = require('net'); + +// This test sends an invalid character to a HTTP server and purposely +// does not handle clientError (even if it sets an event handler). +// +// The idea is to let the server emit multiple errors on the socket, +// mostly due to parsing error, and make sure they don't result +// in leaking event listeners. + +let i = 0; +let socket; + +process.on('warning', common.mustNotCall()); + +const server = http.createServer(common.mustNotCall()); + +server.on('clientError', common.mustCallAtLeast((err) => { + assert.strictEqual(err.code, 'HPE_INVALID_METHOD'); + assert.strictEqual(err.rawPacket.toString(), '*'); + + if (i === 20) { + socket.end(); + } else { + socket.write('*'); + i++; + } +}, 1)); + +server.listen(0, () => { + socket = net.createConnection({ port: server.address().port }); + + socket.on('connect', () => { + socket.write('*'); + }); + + socket.on('close', () => { + server.close(); + }); +});