diff --git a/lib/_http_server.js b/lib/_http_server.js index d44257287a2915..1432ac8e637ad9 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -766,7 +766,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 00000000000000..2513bc1a90c9f6 --- /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(); + }); +});