Skip to content

'close' event called before 'end' in paused stream #19166

Description

@DiegoTUI

I believe I've found an akward behaviour of a stream in paused mode using the net module. In some specific cases, when the source socket ends, the close event is called before the end event in the destination socket. I've been able to reproduce it simulating the callback queue with nested setImmediates.

Server

'use strict';
                               
const net = require('net');    

let n=0;                       

const opts = {                 
  host: '127.0.0.1',
  port: 1100
};
  
const server = net.createServer(s => {
  let id = n++;                
  s.setEncoding('utf8');       
  s.on('readable', () => {
    setImmediate(() => 
      setImmediate(() => 
        setImmediate(() =>
          setImmediate(() => s.read()))));
  });
  s.on('end', () => console.trace(`Socket ${id} END`));
  s.on('close', () => console.trace(`Socket ${id} CLOSE`));
});

server.listen(opts, console.info.bind(null, 'server listening', opts));

I've tried with two clients.

Client 1 (bash)
$ echo EVENT | nc localhost 1100

Client 2 (node)

'use strict';

const net = require('net');

let n=0;

function runOnce() { 
  const s = net.connect(1100, () => { 
    console.info('Connected', n++);
    s.write('EVENT', () => { 
      s.end();
    });
  });
  return new Promise(ok => { 
    s.on('close', ok)
  })
}

async function run(times) {
  for (let i=0; i<times; i++) { 
    await runOnce();
  }
}

run(10);

It consistently "fails" with both, "fail" meaning that the close event is called before the end event. According to the documentation this should never happen.

The 'close' event is emitted when the stream and any of its underlying resources (a file descriptor, for example) have been closed. The event indicates that no more events will be emitted, and no further computation will occur.

NOTE: The number of setImmediates that you need to nest in order to make it "fail" may vary depending on your environment.

Activity

  1. rohanraj7316 commented on Mar 6, 2018

    @rohanraj7316

    end event triggers at the ending of the readable side of the socket execute completely.
    so, it's not necessary that trigger of close and end events should be sequential.
    @DiegoTUI

  2. DiegoTUI commented on Mar 6, 2018

    @DiegoTUI
    ContributorAuthor

    @rohanraj7316 but according to the docs, once the close event is emitted, no more events are emitted, right? It seems that in this case, the end event is emitted after the close event contradicting the docs.

  3. richardlau commented on Mar 6, 2018

    @richardlau
    Member

    cc @nodejs/streams

  4. rohanraj7316 commented on Mar 6, 2018

    @rohanraj7316

    can you share the link pls @DiegoTUI

  5. DiegoTUI commented on Mar 6, 2018

    @DiegoTUI
    ContributorAuthor
  6. rohanraj7316 commented on Mar 6, 2018

    @rohanraj7316

    Since we have multiple phases in the Node js event loop.
    Close event is not behaving in the way you are
    expecting it to be because of the absence of stream callback.

    When we say event loop there are multiple phases of Event Loop cycle
    all the callback corresponding to given event are present in that phase.
    but since you are adding the listener to the net modules object it is acting
    according to the net modules close and end callbacks. I guess this can
    be one of the possible reason. for more details regarding the behavior of
    event you can go through this link
    @DiegoTUI

  7. DiegoTUI commented on Mar 7, 2018

    @DiegoTUI
    ContributorAuthor

    @rohanraj7316 If I understood correctly, you are suggesting that the fact that the end and close events come in the wrong order could be due to the fact that I am listening to the end and close events of a net.Socket, and not of a "pure" stream. It doesn't make a lot of sense to me because a Socket is a Duplex stream, and it seems that it relies in the stream's event to produce his own events. The docs of the Socket close event are also pretty clear about this:

    Emitted once the socket is fully closed. The argument had_error is a boolean which says if the socket was closed due to a transmission error.

    If the close event is emitted, then the socket is fully closed. It doesn't make any sense that the end event is emitted after the socket is fully closed.

  8. mcollina commented on Mar 7, 2018

    @mcollina
    SponsorMember

    I think this is a bug. It also goes back to Node 4 at least.

  9. added
    streamIssues and PRs related to Node.js streams.
    confirmed-bugIssues and PRs for confirmed bugs.
    on Mar 7, 2018
  10. added a commit that references this issue on Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    confirmed-bugIssues and PRs for confirmed bugs.streamIssues and PRs related to Node.js streams.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions