-
-
Notifications
You must be signed in to change notification settings - Fork 33.5k
Description
Version
v16.13.0 / v17.1.0
Platform
Darwin Silmaril.local 21.1.0 Darwin Kernel Version 21.1.0: Wed Oct 13 17:33:24 PDT 2021; root:xnu-8019.41.5~1/RELEASE_ARM64_T8101 arm64
Subsystem
http
What steps will reproduce the bug?
'use strict';
const http = require('http');
let clientReq;
const server = http.createServer(function (req, res) {
req.on('aborted', function () {
console.log('ABORTED');
server.close();
});
req.on('end', () => {
console.log('DESTROY');
clientReq.destroy();
});
req.resume(); // read all client data
});
server.listen(0, () => {
clientReq = http.request({
method: 'POST',
port: server.address().port,
headers: { connection: 'keep-alive' }
}, (res) => {
});
clientReq.on('error', (err) => console.log('CLIENT ERROR', err));
clientReq.end();
});
How often does it reproduce? Is there a required condition?
100%
What is the expected behavior?
Script exits and logs the following (node v14.18.1):
DESTROY
CLIENT ERROR Error: socket hang up
at connResetException (internal/errors.js:639:14)
at Socket.socketCloseListener (_http_client.js:449:25)
at Socket.emit (events.js:412:35)
at TCP.<anonymous> (net.js:686:12) {
code: 'ECONNRESET'
}
ABORTED
What do you see instead?
Script never exits and misses the last ABORTED
line from the log:
DESTROY
CLIENT ERROR Error: socket hang up
at connResetException (node:internal/errors:691:14)
at Socket.socketCloseListener (node:_http_client:420:25)
at Socket.emit (node:events:402:35)
at TCP.<anonymous> (node:net:687:12) {
code: 'ECONNRESET'
}
Additional information
This seems to be a fallout for #33035, which had to be reverted in the v15 release line, but now included in v16+. I discovered it while investigating hapijs/hapi#4315.
Essentially 'close'
is now emitted on IncomingMessage
as soon as all data has been consumed, regardless of whether the underlying stream is still open (ie. the response is still being delivered). Thus node is clearly violating its own streaming API, since the underlying socket is still very much active, and will occasionally need to signal 'aborted'
on the req
object.
I expect the proper solution is to delay the 'close'
event until res
has also finished. This should also fix related issues, like #38924.
/cc @dnlup