Browse Source

tls: proper .destroySoon

1. Emit `sslOutEnd` only when `_internallyPendingBytes() === 0`.
2. Read before checking `._halfRead`, otherwise we'll see only previous
   value, and will invoke `._write` callback improperly.
3. Wait for both `end` and `finish` events in `.destroySoon`.
4. Unpipe encrypted stream from socket to prevent write after destroy.
v0.10.9-release
Fedor Indutny 12 years ago
parent
commit
9ee86b718c
  1. 55
      lib/tls.js

55
lib/tls.js

@ -362,6 +362,16 @@ CryptoStream.prototype._write = function write(data, encoding, cb) {
return cb(this.pair.error(true)); return cb(this.pair.error(true));
} }
// Force SSL_read call to cycle some states/data inside OpenSSL
this.pair.cleartext.read(0);
// Cycle encrypted data
if (this.pair.encrypted._internallyPendingBytes())
this.pair.encrypted.read(0);
// Get NPN and Server name when ready
this.pair.maybeInitFinished();
// Whole buffer was written // Whole buffer was written
if (written === data.length) { if (written === data.length) {
if (this === this.pair.cleartext) { if (this === this.pair.cleartext) {
@ -377,19 +387,6 @@ CryptoStream.prototype._write = function write(data, encoding, cb) {
} else { } else {
cb(null); cb(null);
} }
}
// Force SSL_read call to cycle some states/data inside OpenSSL
this.pair.cleartext.read(0);
// Cycle encrypted data
if (this.pair.encrypted._internallyPendingBytes())
this.pair.encrypted.read(0);
// Get NPN and Server name when ready
this.pair.maybeInitFinished();
if (written === data.length) {
return; return;
} else if (written !== 0 && written !== -1) { } else if (written !== 0 && written !== -1) {
assert(!this._retryAfterPartial); assert(!this._retryAfterPartial);
@ -521,13 +518,15 @@ CryptoStream.prototype._read = function read(size) {
this._halfRead = halfRead; this._halfRead = halfRead;
// Notify listeners about internal data end // Notify listeners about internal data end
if (this === this.pair.cleartext) { if (!halfRead) {
debug('cleartext.sslOutEnd'); if (this === this.pair.cleartext) {
} else { debug('cleartext.sslOutEnd');
debug('encrypted.sslOutEnd'); } else {
} debug('encrypted.sslOutEnd');
}
this.emit('sslOutEnd'); this.emit('sslOutEnd');
}
} }
}; };
@ -640,10 +639,19 @@ CryptoStream.prototype.destroySoon = function(err) {
if (this.writable) if (this.writable)
this.end(); this.end();
if (this._writableState.finished && this._opposite._ended) if (this._writableState.finished && this._opposite._ended) {
this.destroy(); this.destroy();
else } else {
this.once('finish', this.destroy); // Wait for both `finish` and `end` events to ensure that all data that
// was written on this side was read from the other side.
var self = this;
var waiting = 2;
function finish() {
if (--waiting === 0) self.destroy();
}
this._opposite.once('end', finish);
this.once('finish', finish);
}
}; };
@ -1379,6 +1387,9 @@ function pipe(pair, socket) {
pair.encrypted.on('close', function() { pair.encrypted.on('close', function() {
process.nextTick(function() { process.nextTick(function() {
// Encrypted should be unpiped from socket to prevent possible
// write after destroy.
pair.encrypted.unpipe(socket);
socket.destroy(); socket.destroy();
}); });
}); });

Loading…
Cancel
Save