net: autoDestroy Socket

Refactors net.Socket into using autoDestroy functionality
of streams.

PR-URL: https://github.com/nodejs/node/pull/31806
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Denys Otrishko <shishugi@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
This commit is contained in:
Robert Nagy 2020-02-15 01:56:01 +01:00
parent 882b61a7ee
commit efefdd668d
5 changed files with 22 additions and 38 deletions

View File

@ -285,20 +285,15 @@ function Socket(options) {
else else
options = { ...options }; options = { ...options };
const { allowHalfOpen } = options; // Default to *not* allowing half open sockets.
options.allowHalfOpen = Boolean(options.allowHalfOpen);
// Prevent the "no-half-open enforcer" from being inherited from `Duplex`.
options.allowHalfOpen = true;
// For backwards compat do not emit close on destroy. // For backwards compat do not emit close on destroy.
options.emitClose = false; options.emitClose = false;
options.autoDestroy = false; options.autoDestroy = true;
// Handle strings directly. // Handle strings directly.
options.decodeStrings = false; options.decodeStrings = false;
stream.Duplex.call(this, options); stream.Duplex.call(this, options);
// Default to *not* allowing half open sockets.
this.allowHalfOpen = Boolean(allowHalfOpen);
if (options.handle) { if (options.handle) {
this._handle = options.handle; // private this._handle = options.handle; // private
this[async_id_symbol] = getNewAsyncId(this._handle); this[async_id_symbol] = getNewAsyncId(this._handle);
@ -416,28 +411,18 @@ Socket.prototype._final = function(cb) {
const err = this._handle.shutdown(req); const err = this._handle.shutdown(req);
if (err === 1 || err === UV_ENOTCONN) // synchronous finish if (err === 1 || err === UV_ENOTCONN) // synchronous finish
return afterShutdown.call(req, 0); return cb();
else if (err !== 0) else if (err !== 0)
return this.destroy(errnoException(err, 'shutdown')); return cb(errnoException(err, 'shutdown'));
}; };
function afterShutdown() {
function afterShutdown(status) {
const self = this.handle[owner_symbol]; const self = this.handle[owner_symbol];
debug('afterShutdown destroyed=%j', self.destroyed, debug('afterShutdown destroyed=%j', self.destroyed,
self._readableState); self._readableState);
this.callback(); this.callback();
// Callback may come after call to destroy.
if (self.destroyed)
return;
if (!self.readable || self.readableEnded) {
debug('readableState ended, destroying');
self.destroy();
}
} }
// Provide a better error message when we call end() as a result // Provide a better error message when we call end() as a result
@ -452,10 +437,10 @@ function writeAfterFIN(chunk, encoding, cb) {
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
const er = new Error('This socket has been ended by the other party'); const er = new Error('This socket has been ended by the other party');
er.code = 'EPIPE'; er.code = 'EPIPE';
process.nextTick(emitErrorNT, this, er);
if (typeof cb === 'function') { if (typeof cb === 'function') {
defaultTriggerAsyncIdScope(this[async_id_symbol], process.nextTick, cb, er); defaultTriggerAsyncIdScope(this[async_id_symbol], process.nextTick, cb, er);
} }
this.destroy(er);
return false; return false;
} }
@ -628,12 +613,7 @@ Socket.prototype.read = function(n) {
function onReadableStreamEnd() { function onReadableStreamEnd() {
if (!this.allowHalfOpen) { if (!this.allowHalfOpen) {
this.write = writeAfterFIN; this.write = writeAfterFIN;
if (this.writable) }
this.end();
else if (!this.writableLength)
this.destroy();
} else if (!this.destroyed && !this.writable && !this.writableLength)
this.destroy();
} }

View File

@ -23,7 +23,10 @@ server.on('stream', (stream) => {
server.listen(0, () => { server.listen(0, () => {
const h2header = Buffer.alloc(9); const h2header = Buffer.alloc(9);
const conn = net.connect(server.address().port); const conn = net.connect({
port: server.address().port,
allowHalfOpen: true
});
conn.write('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'); conn.write('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n');

View File

@ -21,6 +21,7 @@
'use strict'; 'use strict';
const common = require('../common'); const common = require('../common');
const assert = require('assert');
const net = require('net'); const net = require('net');
@ -31,10 +32,7 @@ const server = net.createServer(common.mustCall(function(socket) {
socket.resume(); socket.resume();
socket.on('error', common.mustCall(function(error) { socket.on('error', common.mustNotCall());
console.error('received error as expected, closing server', error);
server.close();
}));
})); }));
server.listen(0, function() { server.listen(0, function() {
@ -44,7 +42,10 @@ server.listen(0, function() {
// Then 'end' will be emitted when it receives a FIN packet from // Then 'end' will be emitted when it receives a FIN packet from
// the other side. // the other side.
client.on('end', common.mustCall(() => { client.on('end', common.mustCall(() => {
serverSocket.write('test', common.mustCall()); serverSocket.write('test', common.mustCall((err) => {
assert(err);
server.close();
}));
})); }));
client.end(); client.end();
}); });

View File

@ -34,7 +34,8 @@ const server = tls.createServer(serverConfig, common.mustCall(function() {
secureProtocol: v.secureProtocol secureProtocol: v.secureProtocol
}, common.mustCall(function() { }, common.mustCall(function() {
assert.strictEqual(this.getProtocol(), v.version); assert.strictEqual(this.getProtocol(), v.version);
this.on('end', common.mustCall(function() { this.on('end', common.mustCall());
this.on('close', common.mustCall(function() {
assert.strictEqual(this.getProtocol(), null); assert.strictEqual(this.getProtocol(), null);
})).end(); })).end();
if (++connected === clientConfigs.length) if (++connected === clientConfigs.length)

View File

@ -59,9 +59,8 @@ const net = require('net');
assert.strictEqual(client.bufferSize, i + 1); assert.strictEqual(client.bufferSize, i + 1);
} }
// It seems that tlsSockets created from sockets of `Duplex` emit no client.on('end', common.mustCall());
// "finish" events. We use "end" event instead. client.on('close', common.mustCall(() => {
client.on('end', common.mustCall(() => {
assert.strictEqual(client.bufferSize, undefined); assert.strictEqual(client.bufferSize, undefined);
})); }));