diff --git a/lib/_stream_duplex.js b/lib/_stream_duplex.js index 1ccb931260ddbd..522b94d660c821 100644 --- a/lib/_stream_duplex.js +++ b/lib/_stream_duplex.js @@ -29,33 +29,15 @@ module.exports = Duplex; const util = require('util'); -const Readable = require('_stream_readable'); -const Writable = require('_stream_writable'); - -util.inherits(Duplex, Readable); - -{ - // avoid scope creep, the keys array can then be collected - const keys = Object.keys(Writable.prototype); - for (var v = 0; v < keys.length; v++) { - const method = keys[v]; - if (!Duplex.prototype[method]) - Duplex.prototype[method] = Writable.prototype[method]; - } -} +const DuplexBase = require('internal/streams/duplex_base'); + +util.inherits(Duplex, DuplexBase); function Duplex(options) { if (!(this instanceof Duplex)) return new Duplex(options); - Readable.call(this, options); - Writable.call(this, options); - - if (options && options.readable === false) - this.readable = false; - - if (options && options.writable === false) - this.writable = false; + DuplexBase.call(this, options); this.allowHalfOpen = true; if (options && options.allowHalfOpen === false) { diff --git a/lib/internal/streams/duplex_base.js b/lib/internal/streams/duplex_base.js new file mode 100644 index 00000000000000..df3c5c37a80ea7 --- /dev/null +++ b/lib/internal/streams/duplex_base.js @@ -0,0 +1,30 @@ +'use strict'; + +const util = require('util'); +const Readable = require('_stream_readable'); +const Writable = require('_stream_writable'); + +function DuplexBase(options) { + Readable.call(this, options); + Writable.call(this, options); + + if (options && options.readable === false) + this.readable = false; + + if (options && options.writable === false) + this.writable = false; +} + +util.inherits(DuplexBase, Readable); + +{ + // Avoid scope creep, the keys array can then be collected. + const keys = Object.keys(Writable.prototype); + for (var v = 0; v < keys.length; v++) { + const method = keys[v]; + if (!DuplexBase.prototype[method]) + DuplexBase.prototype[method] = Writable.prototype[method]; + } +} + +module.exports = DuplexBase; diff --git a/lib/net.js b/lib/net.js index f2cb423f3003ea..7fa1e20aedaec5 100644 --- a/lib/net.js +++ b/lib/net.js @@ -64,6 +64,7 @@ const { ERR_SOCKET_BAD_PORT, ERR_SOCKET_CLOSED } = errors.codes; +const DuplexBase = require('internal/streams/duplex_base'); const dns = require('dns'); const kLastWriteQueueSize = Symbol('lastWriteQueueSize'); @@ -238,7 +239,11 @@ function Socket(options) { // For backwards compat do not emit close on destroy. options.emitClose = false; - stream.Duplex.call(this, options); + // `DuplexBase` is just a slimmed down constructor for `Duplex` which allow + // us to not inherit the "no-half-open enforcer" as there is already one in + // place. Instances of `Socket` are still instances of `Duplex`, that is, + // `socket instanceof Duplex === true`. + DuplexBase.call(this, options); if (options.handle) { this._handle = options.handle; // private @@ -263,8 +268,6 @@ function Socket(options) { this._writev = null; this._write = makeSyncWrite(fd); } - this.readable = options.readable !== false; - this.writable = options.writable !== false; } else { // these will be set once there is a connection this.readable = this.writable = false; @@ -282,7 +285,7 @@ function Socket(options) { this._writableState.decodeStrings = false; // default to *not* allowing half open sockets - this.allowHalfOpen = options && options.allowHalfOpen || false; + this.allowHalfOpen = options.allowHalfOpen || false; // if we have a handle, then start the flow of data into the // buffer. if not, then this will happen when we connect diff --git a/node.gyp b/node.gyp index 3d5fd72958399c..ce8125ecc39d39 100644 --- a/node.gyp +++ b/node.gyp @@ -146,6 +146,7 @@ 'lib/internal/streams/lazy_transform.js', 'lib/internal/streams/async_iterator.js', 'lib/internal/streams/BufferList.js', + 'lib/internal/streams/duplex_base.js', 'lib/internal/streams/duplexpair.js', 'lib/internal/streams/legacy.js', 'lib/internal/streams/destroy.js', diff --git a/test/parallel/test-http-connect.js b/test/parallel/test-http-connect.js index f90d235521a649..ec2c8846bbb134 100644 --- a/test/parallel/test-http-connect.js +++ b/test/parallel/test-http-connect.js @@ -75,12 +75,7 @@ server.listen(0, common.mustCall(() => { assert.strictEqual(socket.listeners('connect').length, 0); assert.strictEqual(socket.listeners('data').length, 0); assert.strictEqual(socket.listeners('drain').length, 0); - - // the stream.Duplex onend listener - // allow 0 here, so that i can run the same test on streams1 impl - assert(socket.listenerCount('end') <= 2, - `Found ${socket.listenerCount('end')} end listeners`); - + assert.strictEqual(socket.listeners('end').length, 1); assert.strictEqual(socket.listeners('free').length, 0); assert.strictEqual(socket.listeners('close').length, 0); assert.strictEqual(socket.listeners('error').length, 0); diff --git a/test/parallel/test-net-socket-no-halfopen-enforcer.js b/test/parallel/test-net-socket-no-halfopen-enforcer.js new file mode 100644 index 00000000000000..3df5b6d7b9c8cb --- /dev/null +++ b/test/parallel/test-net-socket-no-halfopen-enforcer.js @@ -0,0 +1,11 @@ +'use strict'; +require('../common'); + +// This test ensures that `net.Socket` does not inherit the no-half-open +// enforcer from `stream.Duplex`. + +const { Socket } = require('net'); +const { strictEqual } = require('assert'); + +const socket = new Socket({ allowHalfOpen: false }); +strictEqual(socket.listenerCount('end'), 1);