Description
Version
v21.6.0
Platform
Darwin Kernel Version 19.6.0
Subsystem
none
What steps will reproduce the bug?
Run the script below:
const fs = require('fs');
const https = require('https');
const http2 = require('http2');
const USE_COMPATIBILITY_MODE = true;
(async function main() {
const sockets = new Set();
let server;
if (USE_COMPATIBILITY_MODE) {
server = http2.createSecureServer({
allowHTTP1: true,
cert: fs.readFileSync('test-cert.pem'),
key: fs.readFileSync('test-key.pem'),
});
} else {
server = https.createServer({
cert: fs.readFileSync('test-cert.pem'),
key: fs.readFileSync('test-key.pem'),
});
}
server.on('connection', (socket) => {
sockets.add(socket);
socket.on('close', () => sockets.delete(socket));
});
server.on('request', (req, res) => {
console.log('request received');
const message = 'hello world.';
res.setHeader('Content-Length', String(Buffer.byteLength(message)));
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.writeHead(200);
res.end(message);
});
await new Promise(resolve => server.listen(443, resolve));
console.log('Listening at https://localhost:443');
await openKeepAliveSocket();
console.log(`closing ${sockets.size} socket(s)...`);
server.close(() => console.log('done'));
})();
function openKeepAliveSocket() {
return new Promise((resolve, reject) => {
const req = https.get('https://localhost:443', {
rejectUnauthorized: false,
headers: { connection: 'keep-alive' },
});
req.on('error', reject);
req.on('response', (res) => {
console.log('response status ' + res.statusCode);
res.on('error', reject);
res.on('data', () => {});
res.on('end', () => {
console.log('response ended');
resolve();
});
});
});
}
How often does it reproduce? Is there a required condition?
I can reliably reproduce it 100% of the time.
What is the expected behavior? Why is that the expected behavior?
The expected behavior is for the HTTPS server to close down immediately (as soon as the request ends). This is what happens when using https.createServer()
, as demonstrated by changing USE_COMPATIBILITY_MODE
to false
in the script above.
What do you see instead?
Instead, the HTTPS server stays open for 5 seconds (which is the default keep-alive duration).
In other words, calling server.close()
properly shuts down idle "keep-alive" sockets when using the http
or https
modules, but not when using the http2
module with allowHTTP1: true
.
Additional information
You'll need a TLS certificate to run the script above. I created one by following this guide in the Node.js documentation to create a self-signed certificate.