Skip to content

Commit eeccd52

Browse files
committedMar 24, 2020
net: make readable/writable start as true
`net.Socket` is slightly breaking stream invariants by having readable/writable going from `false` to `true`. Streams assume that readable/writable starts out `true` and then goes to `false` through `push(null)`/`end()` after which it never goes back to `true`, e.g. once a stream is `writable == false` it is assumed it will never become `true`. This PR changes 2 things: Unless explicitly set to `false` through options: - starts as `readable`/`writable` `true` by default. - uses `push(null)`/`end()` to set `readable`/`writable` to `false`. Note that this would cause the socket to emit the `'end'`/`'finish'` events, which it did not do previously. In the case it is explicitly set to `false` through options` it is assumed to never become `true`. PR-URL: nodejs#32272 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 96f06e6 commit eeccd52

6 files changed

+35
-33
lines changed
 

‎lib/_stream_readable.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
const {
2525
ArrayIsArray,
26-
Boolean,
2726
NumberIsInteger,
2827
NumberIsNaN,
2928
ObjectDefineProperties,
@@ -1061,9 +1060,12 @@ ObjectDefineProperties(Readable.prototype, {
10611060
readable: {
10621061
get() {
10631062
const r = this._readableState;
1064-
if (!r) return false;
1065-
if (r.readable !== undefined) return r.readable && !r.endEmitted;
1066-
return Boolean(!r.destroyed && !r.errorEmitted && !r.endEmitted);
1063+
// r.readable === false means that this is part of a Duplex stream
1064+
// where the readable side was disabled upon construction.
1065+
// Compat. The user might manually disable readable side through
1066+
// deprecated setter.
1067+
return !!r && r.readable !== false && !r.destroyed && !r.errorEmitted &&
1068+
!r.endEmitted;
10671069
},
10681070
set(val) {
10691071
// Backwards compat.

‎lib/_stream_writable.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727

2828
const {
2929
Array,
30-
Boolean,
3130
FunctionPrototype,
3231
ObjectDefineProperty,
3332
ObjectDefineProperties,
@@ -744,9 +743,12 @@ ObjectDefineProperties(Writable.prototype, {
744743
writable: {
745744
get() {
746745
const w = this._writableState;
747-
if (!w) return false;
748-
if (w.writable !== undefined) return w.writable && !w.ended;
749-
return Boolean(!w.destroyed && !w.errored && !w.ending);
746+
// w.writable === false means that this is part of a Duplex stream
747+
// where the writable side was disabled upon construction.
748+
// Compat. The user might manually disable writable side through
749+
// deprecated setter.
750+
return !!w && w.writable !== false && !w.destroyed && !w.errored &&
751+
!w.ending;
750752
},
751753
set(val) {
752754
// Backwards compatible.

‎lib/_tls_wrap.js

+1-8
Original file line numberDiff line numberDiff line change
@@ -494,9 +494,7 @@ function TLSSocket(socket, opts) {
494494
handle: this._wrapHandle(wrap),
495495
allowHalfOpen: socket ? socket.allowHalfOpen : tlsOptions.allowHalfOpen,
496496
pauseOnCreate: tlsOptions.pauseOnConnect,
497-
// The readable flag is only needed if pauseOnCreate will be handled.
498-
readable: tlsOptions.pauseOnConnect,
499-
writable: false
497+
manualStart: true
500498
});
501499

502500
// Proxy for API compatibility
@@ -506,11 +504,6 @@ function TLSSocket(socket, opts) {
506504

507505
this._init(socket, wrap);
508506

509-
// Make sure to setup all required properties like: `connecting` before
510-
// starting the flow of the data
511-
this.readable = true;
512-
this.writable = true;
513-
514507
if (enableTrace && this._handle)
515508
this._handle.enableTrace();
516509

‎lib/net.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -285,8 +285,6 @@ function Socket(options) {
285285
else
286286
options = { ...options };
287287

288-
options.readable = options.readable || false;
289-
options.writable = options.writable || false;
290288
const { allowHalfOpen } = options;
291289

292290
// Prevent the "no-half-open enforcer" from being inherited from `Duplex`.
@@ -655,8 +653,6 @@ Socket.prototype._destroy = function(exception, cb) {
655653

656654
this.connecting = false;
657655

658-
this.readable = this.writable = false;
659-
660656
for (let s = this; s !== null; s = s._parent) {
661657
clearTimeout(s[kTimeout]);
662658
}
@@ -1120,9 +1116,13 @@ function afterConnect(status, handle, req, readable, writable) {
11201116
self._sockname = null;
11211117

11221118
if (status === 0) {
1123-
self.readable = readable;
1124-
if (!self._writableState.ended)
1125-
self.writable = writable;
1119+
if (self.readable && !readable) {
1120+
self.push(null);
1121+
self.read();
1122+
}
1123+
if (self.writable && !writable) {
1124+
self.end();
1125+
}
11261126
self._unrefTimer();
11271127

11281128
self.emit('connect');

‎test/parallel/test-net-socket-constructor.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ function test(sock, readable, writable) {
2727
}
2828

2929
if (cluster.isMaster) {
30-
test(undefined, false, false);
30+
test(undefined, true, true);
3131

3232
const server = net.createServer(common.mustCall((socket) => {
3333
socket.unref();
3434
test(socket, true, true);
35-
test({ handle: socket._handle }, false, false);
35+
test({ handle: socket._handle }, true, true);
3636
test({ handle: socket._handle, readable: true, writable: true },
3737
true, true);
3838
server.close();
@@ -45,7 +45,7 @@ if (cluster.isMaster) {
4545
socket.end();
4646
}));
4747

48-
test(socket, false, true);
48+
test(socket, true, true);
4949
}));
5050

5151
cluster.setupMaster({
@@ -58,8 +58,8 @@ if (cluster.isMaster) {
5858
assert.strictEqual(signal, null);
5959
}));
6060
} else {
61-
test(4, false, false);
62-
test({ fd: 5 }, false, false);
61+
test(4, true, true);
62+
test({ fd: 5 }, true, true);
6363
test({ fd: 6, readable: true, writable: true }, true, true);
6464
process.disconnect();
6565
}

‎test/parallel/test-net-socket-setnodelay.js

+10-5
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,32 @@ const genSetNoDelay = (desiredArg) => (enable) => {
1313
// setNoDelay should default to true
1414
let socket = new net.Socket({
1515
handle: {
16-
setNoDelay: common.mustCall(genSetNoDelay(true))
16+
setNoDelay: common.mustCall(genSetNoDelay(true)),
17+
readStart() {}
1718
}
1819
});
1920
socket.setNoDelay();
2021

2122
socket = new net.Socket({
2223
handle: {
23-
setNoDelay: common.mustCall(genSetNoDelay(true), 1)
24+
setNoDelay: common.mustCall(genSetNoDelay(true), 1),
25+
readStart() {}
2426
}
2527
});
2628
truthyValues.forEach((testVal) => socket.setNoDelay(testVal));
2729

2830
socket = new net.Socket({
2931
handle: {
30-
setNoDelay: common.mustNotCall()
32+
setNoDelay: common.mustNotCall(),
33+
readStart() {}
3134
}
3235
});
3336
falseyValues.forEach((testVal) => socket.setNoDelay(testVal));
3437

3538
socket = new net.Socket({
3639
handle: {
37-
setNoDelay: common.mustCall(() => {}, 3)
40+
setNoDelay: common.mustCall(() => {}, 3),
41+
readStart() {}
3842
}
3943
});
4044
truthyValues.concat(falseyValues).concat(truthyValues)
@@ -44,7 +48,8 @@ truthyValues.concat(falseyValues).concat(truthyValues)
4448
// In the case below, if it is called an exception will be thrown
4549
socket = new net.Socket({
4650
handle: {
47-
setNoDelay: null
51+
setNoDelay: null,
52+
readStart() {}
4853
}
4954
});
5055
const returned = socket.setNoDelay(true);

0 commit comments

Comments
 (0)
Please sign in to comment.