diff --git a/lib/_http_server.js b/lib/_http_server.js index c010d8fa96a1c2..87a91eafd0de87 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -493,14 +493,16 @@ function storeHTTPOptions(options) { } } -function setupConnectionsTracking(server) { +function setupConnectionsTracking() { // Start connection handling - server[kConnections] = new ConnectionsList(); + if (!this[kConnections]) { + this[kConnections] = new ConnectionsList(); + } // This checker is started without checking whether any headersTimeout or requestTimeout is non zero // otherwise it would not be started if such timeouts are modified after createServer. - server[kConnectionsCheckingInterval] = - setInterval(checkConnections.bind(server), server.connectionsCheckingInterval).unref(); + this[kConnectionsCheckingInterval] = + setInterval(checkConnections.bind(this), this.connectionsCheckingInterval).unref(); } function Server(options, requestListener) { @@ -533,11 +535,12 @@ function Server(options, requestListener) { this.httpAllowHalfOpen = false; this.on('connection', connectionListener); + this.on('listening', setupConnectionsTracking); this.timeout = 0; this.maxHeadersCount = null; this.maxRequestsPerSocket = 0; - setupConnectionsTracking(this); + this[kUniqueHeaders] = parseUniqueHeadersOption(options.uniqueHeaders); } ObjectSetPrototypeOf(Server.prototype, net.Server.prototype); @@ -549,6 +552,10 @@ Server.prototype.close = function() { }; Server.prototype.closeAllConnections = function() { + if (!this[kConnections]) { + return; + } + const connections = this[kConnections].all(); for (let i = 0, l = connections.length; i < l; i++) { @@ -557,6 +564,10 @@ Server.prototype.closeAllConnections = function() { }; Server.prototype.closeIdleConnections = function() { + if (!this[kConnections]) { + return; + } + const connections = this[kConnections].idle(); for (let i = 0, l = connections.length; i < l; i++) { diff --git a/lib/https.js b/lib/https.js index 12453609f2c452..443cb694d618b6 100644 --- a/lib/https.js +++ b/lib/https.js @@ -86,8 +86,9 @@ function Server(opts, requestListener) { this.timeout = 0; this.maxHeadersCount = null; - setupConnectionsTracking(this); + this.on('listening', setupConnectionsTracking); } + ObjectSetPrototypeOf(Server.prototype, tls.Server.prototype); ObjectSetPrototypeOf(Server, tls.Server); diff --git a/test/parallel/test-http-server-connections-checking-leak.js b/test/parallel/test-http-server-connections-checking-leak.js new file mode 100644 index 00000000000000..e28cf117c65f87 --- /dev/null +++ b/test/parallel/test-http-server-connections-checking-leak.js @@ -0,0 +1,24 @@ +'use strict'; + +// Flags: --expose-gc + +// Check that creating a server without listening does not leak resources. + +require('../common'); +const onGC = require('../common/ongc'); +const Countdown = require('../common/countdown'); + +const http = require('http'); +const max = 100; + +// Note that Countdown internally calls common.mustCall, that's why it's not done here. +const countdown = new Countdown(max, () => {}); + +for (let i = 0; i < max; i++) { + const server = http.createServer((req, res) => {}); + onGC(server, { ongc: countdown.dec.bind(countdown) }); +} + +setImmediate(() => { + global.gc(); +}); diff --git a/test/parallel/test-https-server-connections-checking-leak.js b/test/parallel/test-https-server-connections-checking-leak.js new file mode 100644 index 00000000000000..3e7c45e4660ed4 --- /dev/null +++ b/test/parallel/test-https-server-connections-checking-leak.js @@ -0,0 +1,29 @@ +'use strict'; + +// Flags: --expose-gc + +// Check that creating a server without listening does not leak resources. + +const common = require('../common'); + +if (!common.hasCrypto) { + common.skip('missing crypto'); +} + +const onGC = require('../common/ongc'); +const Countdown = require('../common/countdown'); + +const https = require('https'); +const max = 100; + +// Note that Countdown internally calls common.mustCall, that's why it's not done here. +const countdown = new Countdown(max, () => {}); + +for (let i = 0; i < max; i++) { + const server = https.createServer((req, res) => {}); + onGC(server, { ongc: countdown.dec.bind(countdown) }); +} + +setImmediate(() => { + global.gc(); +});