Skip to content

Commit 4cdf9d4

Browse files
committed
tls: Improve TLS flow control
Fixes #1775.
1 parent 49ac083 commit 4cdf9d4

File tree

1 file changed

+41
-7
lines changed

1 file changed

+41
-7
lines changed

lib/tls.js

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ function CryptoStream(pair) {
5252
this.readable = this.writable = true;
5353

5454
this._paused = false;
55+
this._needDrain = false;
5556
this._pending = [];
5657
this._pendingCallbacks = [];
5758
this._pendingBytes = 0;
@@ -86,7 +87,7 @@ CryptoStream.prototype.write = function(data /* , encoding, cb */) {
8687
data = new Buffer(data, encoding);
8788
}
8889

89-
debug('clearIn data');
90+
debug((this === this.pair.cleartext ? 'clear' : 'encrypted') + 'In data');
9091

9192
this._pending.push(data);
9293
this._pendingCallbacks.push(cb);
@@ -95,7 +96,26 @@ CryptoStream.prototype.write = function(data /* , encoding, cb */) {
9596
this.pair._writeCalled = true;
9697
this.pair.cycle();
9798

98-
return this._pendingBytes < 128 * 1024;
99+
// In the following cases, write() should return a false,
100+
// then this stream should eventually emit 'drain' event.
101+
//
102+
// 1. There are pending data more than 128k bytes.
103+
// 2. A forward stream shown below is paused.
104+
// A) EncryptedStream for CleartextStream.write().
105+
// B) CleartextStream for EncryptedStream.write().
106+
//
107+
if (!this._needDrain) {
108+
if (this._pendingBytes >= 128 * 1024) {
109+
this._needDrain = true;
110+
} else {
111+
if (this === this.pair.cleartext) {
112+
this._needDrain = this.pair.encrypted._paused;
113+
} else {
114+
this._needDrain = this.pair.cleartext._paused;
115+
}
116+
}
117+
}
118+
return !this._needDrain;
99119
};
100120

101121

@@ -380,11 +400,25 @@ CryptoStream.prototype._pull = function() {
380400
assert(rv === tmp.length);
381401
}
382402

383-
// If we've cleared all of incoming encrypted data, emit drain.
384-
if (havePending && this._pending.length === 0) {
385-
debug('drain');
386-
this.emit('drain');
387-
if (this.__destroyOnDrain) this.end();
403+
// If pending data has cleared, 'drain' event should be emitted
404+
// after write() returns a false.
405+
// Except when a forward stream shown below is paused.
406+
// A) EncryptedStream for CleartextStream._pull().
407+
// B) CleartextStream for EncryptedStream._pull().
408+
//
409+
if (this._needDrain && this._pending.length === 0) {
410+
var paused;
411+
if (this === this.pair.cleartext) {
412+
paused = this.pair.encrypted._paused;
413+
} else {
414+
paused = this.pair.cleartext._paused;
415+
}
416+
if (!paused) {
417+
debug('drain');
418+
process.nextTick(this.emit.bind(this, 'drain'));
419+
this._needDrain = false;
420+
if (this.__destroyOnDrain) this.end();
421+
}
388422
}
389423
};
390424

0 commit comments

Comments
 (0)