Skip to content

Commit

Permalink
net: emit 'close' after 'end'
Browse files Browse the repository at this point in the history
Currently the writable side of the socket is closed as soon as `UV_EOF`
is read regardless of the state of the socket. This allows the handle
to be closed before `'end'` is emitted and thus `'close'` can be
emitted before `'end'` if the socket is paused.

This commit prevents the handle from being closed until `'end'` is
emitted ensuring the correct order of events.

Fixes: nodejs#19166
  • Loading branch information
lpinca committed Mar 21, 2018
1 parent 81f88e3 commit d25dea7
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 31 deletions.
17 changes: 5 additions & 12 deletions lib/net.js
Original file line number Diff line number Diff line change
Expand Up @@ -373,8 +373,6 @@ function afterShutdown(status, handle) {
if (self._readableState.ended) {
debug('readableState ended, destroying');
self.destroy();
} else {
self.once('_socketEnd', self.destroy);
}
}

Expand Down Expand Up @@ -530,6 +528,11 @@ Socket.prototype.end = function(data, encoding, callback) {

// Called when the 'end' event is emitted.
function onReadableStreamEnd() {
if (!this.allowHalfOpen) {
this.write = writeAfterFIN;
if (this.writable)
this.end();
}
maybeDestroy(this);
}

Expand Down Expand Up @@ -649,16 +652,6 @@ function onread(nread, buffer) {
// `end` -> `close`
self.push(null);
self.read(0);

if (!self.allowHalfOpen) {
self.write = writeAfterFIN;
self.destroySoon();
}

// internal end event so that we know that the actual socket
// is no longer readable, and we can start the shutdown
// procedure. No need to wait for all the data to be consumed.
self.emit('_socketEnd');
}


Expand Down
1 change: 1 addition & 0 deletions test/parallel/test-child-process-fork-net2.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ if (process.argv[2] === 'child') {
console.error('[m] CLIENT: close event');
disconnected += 1;
});
client.resume();
}
});

Expand Down
1 change: 1 addition & 0 deletions test/parallel/test-http2-misbehaving-multiplex.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,5 @@ server.listen(0, () => {
// either way if it is, but we don't want to die if it is.
client.on('error', () => {});
client.on('close', common.mustCall(() => server.close()));
client.resume();
});
4 changes: 4 additions & 0 deletions test/parallel/test-https-client-resume.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,9 @@ server.listen(0, function() {
console.log('close2');
server.close();
});

client2.resume();
});

client1.resume();
});
3 changes: 2 additions & 1 deletion test/parallel/test-net-bytes-read.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const server = net.createServer((socket) => {
assert.strictEqual(socket.bytesRead, prev);
assert.strictEqual(big.length, prev);
}));

socket.end();
});
socket.end();
});
30 changes: 18 additions & 12 deletions test/parallel/test-net-connect-options-path.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,29 @@ const CLIENT_VARIANTS = 12;
});

// CLIENT_VARIANTS depends on the following code
net.connect(serverPath, getConnectCb());
net.connect(serverPath, getConnectCb()).resume();
net.connect(serverPath)
.on('connect', getConnectCb());
net.createConnection(serverPath, getConnectCb());
.on('connect', getConnectCb())
.resume();
net.createConnection(serverPath, getConnectCb()).resume();
net.createConnection(serverPath)
.on('connect', getConnectCb());
new net.Socket().connect(serverPath, getConnectCb());
.on('connect', getConnectCb())
.resume();
new net.Socket().connect(serverPath, getConnectCb()).resume();
new net.Socket().connect(serverPath)
.on('connect', getConnectCb());
net.connect({ path: serverPath }, getConnectCb());
.on('connect', getConnectCb())
.resume();
net.connect({ path: serverPath }, getConnectCb()).resume();
net.connect({ path: serverPath })
.on('connect', getConnectCb());
net.createConnection({ path: serverPath }, getConnectCb());
.on('connect', getConnectCb())
.resume();
net.createConnection({ path: serverPath }, getConnectCb()).resume();
net.createConnection({ path: serverPath })
.on('connect', getConnectCb());
new net.Socket().connect({ path: serverPath }, getConnectCb());
.on('connect', getConnectCb())
.resume();
new net.Socket().connect({ path: serverPath }, getConnectCb()).resume();
new net.Socket().connect({ path: serverPath })
.on('connect', getConnectCb());
.on('connect', getConnectCb())
.resume();
}));
}
18 changes: 12 additions & 6 deletions test/parallel/test-net-connect-options-port.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,27 +102,33 @@ const net = require('net');
function doConnect(args, getCb) {
return [
function createConnectionWithCb() {
return net.createConnection.apply(net, args.concat(getCb()));
return net.createConnection.apply(net, args.concat(getCb()))
.resume();
},
function createConnectionWithoutCb() {
return net.createConnection.apply(net, args)
.on('connect', getCb());
.on('connect', getCb())
.resume();
},
function connectWithCb() {
return net.connect.apply(net, args.concat(getCb()));
return net.connect.apply(net, args.concat(getCb()))
.resume();
},
function connectWithoutCb() {
return net.connect.apply(net, args)
.on('connect', getCb());
.on('connect', getCb())
.resume();
},
function socketConnectWithCb() {
const socket = new net.Socket();
return socket.connect.apply(socket, args.concat(getCb()));
return socket.connect.apply(socket, args.concat(getCb()))
.resume();
},
function socketConnectWithoutCb() {
const socket = new net.Socket();
return socket.connect.apply(socket, args)
.on('connect', getCb());
.on('connect', getCb())
.resume();
}
];
}
Expand Down
2 changes: 2 additions & 0 deletions test/parallel/test-net-server-connections-child-null.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ if (process.argv[2] === 'child') {
assert.strictEqual(server.connections, null);
server.close();
}));

connect.resume();
}));
});

Expand Down
31 changes: 31 additions & 0 deletions test/parallel/test-net-socket-close-after-end.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use strict';
const common = require('../common');

const assert = require('assert');
const net = require('net');

const server = net.createServer();

server.on('connection', (socket) => {
let endEmitted = false;

socket.once('readable', () => {
setTimeout(() => {
socket.read();
}, common.platformTimeout(100));
});
socket.on('end', () => {
endEmitted = true;
});
socket.on('close', () => {
assert(endEmitted);
server.close();
});
socket.end('foo');
});

server.listen(common.mustCall(() => {
const socket = net.createConnection(server.address().port, () => {
socket.end('foo');
});
}));
4 changes: 4 additions & 0 deletions test/parallel/test-tls-client-resume.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,9 @@ server.listen(0, function() {
console.log('close2');
server.close();
});

client2.resume();
});

client1.resume();
});
1 change: 1 addition & 0 deletions test/parallel/test-tls-interleave.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const writes = [
let receivedWrites = 0;

const server = tls.createServer(options, function(c) {
c.resume();
writes.forEach(function(str) {
c.write(str);
});
Expand Down
1 change: 1 addition & 0 deletions test/parallel/test-tls-tlswrap-segfault.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const server = tls.createServer(options, function(s) {
const client = tls.connect(opts, function() {
putImmediate(client);
});
client.resume();
});

function putImmediate(client) {
Expand Down

0 comments on commit d25dea7

Please sign in to comment.