Skip to content

Missing 'aborted' on IncomingMessage (v16 regression) #40775

@kanongil

Description

@kanongil

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    httpIssues or PRs related to the http subsystem.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions